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 2021/07/26 01:12:46 UTC

[logging-log4j2] branch mean-bean-machine updated (7ff8c9c -> 4bf8c3e)

This is an automated email from the ASF dual-hosted git repository.

mattsicker pushed a change to branch mean-bean-machine
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git.


    from 7ff8c9c  Add foundation for bean annotation processing and plugin metadata
     new b4545a4  Add LoggerContextScoped annotation
     new c3c88f5  Add type hierarchy data to plugin modules
     new 2d3e8f0  Use proper producer annotation for bridging APIs
     new 1e33a72  Check for scope of bean class as fallback
     new 4bf8c3e  Add richer plugin bean metadata generation

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 log4j-core/pom.xml                                 |  35 +-
 .../log4j/core/config/di/LoggerContextScoped.java  |   6 +-
 .../core/config/di/impl/DefaultBeanManager.java    |  21 +-
 .../core/config/plugins/PluginBuilderFactory.java  |   4 +-
 .../log4j/core/config/plugins/PluginFactory.java   |   4 +-
 log4j-core/src/main/java9/module-info.java         |  12 +-
 .../log4j/core/config/di/BeanManagerTest.java      |   1 -
 log4j-plugins/pom.xml                              |  80 +---
 .../apache/logging/log4j/plugins/di/Produces.java  |   2 +-
 .../log4j/plugins/di/model/DisposesMethod.java     |  47 +--
 .../log4j/plugins/di/model/GenericPlugin.java      |  36 +-
 .../log4j/plugins/di/model/InjectionTarget.java    |  95 ++---
 .../log4j/plugins/di/model/PluginModule.java       |  17 +-
 .../log4j/plugins/di/model/PluginSource.java       |  16 +-
 .../log4j/plugins/di/model/ProducerField.java      |  49 ++-
 .../log4j/plugins/di/model/ProducerMethod.java     |  62 ++++
 .../log4j/plugins/di/spi/BeanInfoService.java      |  52 ---
 .../log4j/plugins/processor/BeanProcessor.java     | 410 +++++++++++++++------
 log4j-plugins/src/main/java9/module-info.java      |  14 +-
 log4j-plugins/src/test/java-test9/module-info.java |   5 +-
 .../log4j/plugins/processor/BeanProcessorTest.java |  43 ++-
 log4j-plugins/src/test/java9/module-info.java      |  14 +-
 pom.xml                                            |   2 +-
 23 files changed, 566 insertions(+), 461 deletions(-)
 copy log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/SingletonScoped.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/LoggerContextScoped.java (89%)
 copy log4j-1.2-api/src/main/java/org/apache/log4j/RenderedMessage.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/DisposesMethod.java (50%)
 copy log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/MarkerLookup.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/GenericPlugin.java (53%)
 copy log4j-core/src/main/java/org/apache/logging/log4j/core/util/CloseShieldWriter.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/InjectionTarget.java (50%)
 copy log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextShutdownEnabled.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginModule.java (70%)
 copy log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DirectFileRolloverStrategy.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginSource.java (74%)
 copy log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DependentScopeContext.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerField.java (50%)
 create mode 100644 log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerMethod.java
 delete mode 100644 log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/spi/BeanInfoService.java

[logging-log4j2] 04/05: Check for scope of bean class as fallback

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

mattsicker pushed a commit to branch mean-bean-machine
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 1e33a72062f2db54a1504b2393199aed2f8a73a6
Author: Matt Sicker <bo...@gmail.com>
AuthorDate: Sun Jul 25 16:25:02 2021 -0500

    Check for scope of bean class as fallback
---
 .../logging/log4j/core/config/di/impl/DefaultBeanManager.java       | 6 ++++++
 .../org/apache/logging/log4j/core/config/di/BeanManagerTest.java    | 1 -
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java
index 9d9ecce..5e13e18 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java
@@ -353,6 +353,12 @@ public class DefaultBeanManager implements BeanManager {
                 return annotationType;
             }
         }
+        if (element instanceof Field) {
+            return getScopeType(((Field) element).getType());
+        }
+        if (element instanceof Method) {
+            return getScopeType(((Method) element).getReturnType());
+        }
         return DependentScoped.class;
     }
 
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/BeanManagerTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/BeanManagerTest.java
index 1432ea3..3f67266 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/BeanManagerTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/BeanManagerTest.java
@@ -59,7 +59,6 @@ class BeanManagerTest {
 
     @Produces
     @Named
-    @SingletonScoped
     static SingletonBean getSingleton() {
         return new SingletonBean();
     }

[logging-log4j2] 01/05: Add LoggerContextScoped annotation

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

mattsicker pushed a commit to branch mean-bean-machine
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit b4545a4e34754630352daa0c6e8240ebdc9b2ab6
Author: Matt Sicker <bo...@gmail.com>
AuthorDate: Sat Jul 24 16:25:52 2021 -0500

    Add LoggerContextScoped annotation
---
 .../log4j/core/config/di/LoggerContextScoped.java  | 35 ++++++++++++++++++++++
 .../core/config/di/impl/DefaultBeanManager.java    | 15 ++++++----
 2 files changed, 44 insertions(+), 6 deletions(-)

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/LoggerContextScoped.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/LoggerContextScoped.java
new file mode 100644
index 0000000..a601364
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/LoggerContextScoped.java
@@ -0,0 +1,35 @@
+/*
+ * 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.core.config.di;
+
+import org.apache.logging.log4j.plugins.di.ScopeType;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
+@Documented
+@Inherited
+@ScopeType
+public @interface LoggerContextScoped {
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java
index 7dc6921..9d9ecce 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java
@@ -56,6 +56,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -69,10 +70,10 @@ public class DefaultBeanManager implements BeanManager {
 
     private final Injector injector = new Injector(this);
 
-    private final Collection<Bean<?>> enabledBeans = ConcurrentHashMap.newKeySet();
+    private final Collection<Bean<?>> enabledBeans = new HashSet<>();
     private final Map<Type, Collection<Bean<?>>> beansByType = new ConcurrentHashMap<>();
-    private final Collection<DisposesMethod> disposesMethods = Collections.synchronizedCollection(new ArrayList<>());
-    private final Map<Class<? extends Annotation>, ScopeContext> scopes = 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
@@ -409,9 +410,11 @@ public class DefaultBeanManager implements BeanManager {
         beansByType.clear();
         enabledBeans.clear();
         disposesMethods.clear();
-        // TODO: better scope closing after more scopes are supported
-        scopes.get(SingletonScoped.class).close();
-        scopes.clear();
+        final List<Class<? extends Annotation>> scopeTypes = new ArrayList<>(scopes.keySet());
+        Collections.reverse(scopeTypes);
+        for (final Class<? extends Annotation> scopeType : scopeTypes) {
+            scopes.get(scopeType).close();
+        }
     }
 
     private static class DisposesMethod {

[logging-log4j2] 03/05: Use proper producer annotation for bridging APIs

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

mattsicker pushed a commit to branch mean-bean-machine
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 2d3e8f08e57e8fbe7b00566995c8fa9a09beb573
Author: Matt Sicker <bo...@gmail.com>
AuthorDate: Sun Jul 25 16:13:12 2021 -0500

    Use proper producer annotation for bridging APIs
---
 .../logging/log4j/core/config/plugins/PluginBuilderFactory.java       | 4 ++--
 .../org/apache/logging/log4j/core/config/plugins/PluginFactory.java   | 4 ++--
 .../src/main/java/org/apache/logging/log4j/plugins/di/Produces.java   | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java
index 11b2955..9661b8f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java
@@ -17,7 +17,7 @@
 
 package org.apache.logging.log4j.core.config.plugins;
 
-import org.apache.logging.log4j.plugins.di.Produces;
+import org.apache.logging.log4j.plugins.di.Producer;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
@@ -32,7 +32,7 @@ import java.lang.annotation.Target;
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
-@Produces
+@Producer
 public @interface PluginBuilderFactory {
     // empty
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginFactory.java
index abe1cf4..a263fe8 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginFactory.java
@@ -16,7 +16,7 @@
  */
 package org.apache.logging.log4j.core.config.plugins;
 
-import org.apache.logging.log4j.plugins.di.Produces;
+import org.apache.logging.log4j.plugins.di.Producer;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
@@ -35,7 +35,7 @@ import java.lang.annotation.Target;
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
-@Produces
+@Producer
 public @interface PluginFactory {
     // empty
 }
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Produces.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Produces.java
index 2739193..ea73b6c 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Produces.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Produces.java
@@ -45,7 +45,7 @@ import java.lang.annotation.Target;
  * @see <a href="https://docs.jboss.org/cdi/api/2.0/javax/enterprise/inject/Produces.html">CDI @Produces API Docs</a>
  * @see <a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html">Spring @Bean API Docs</a>
  */
-@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
+@Target({ElementType.METHOD, ElementType.FIELD})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 @Producer

[logging-log4j2] 05/05: Add richer plugin bean metadata generation

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

mattsicker pushed a commit to branch mean-bean-machine
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 4bf8c3ea0f78c9e99474dabc0e32cd20b7403390
Author: Matt Sicker <bo...@gmail.com>
AuthorDate: Sun Jul 25 20:12:06 2021 -0500

    Add richer plugin bean metadata generation
---
 .../log4j/plugins/di/model/DisposesMethod.java     |  53 +++
 .../log4j/plugins/di/model/GenericPlugin.java      |  48 +++
 .../log4j/plugins/di/model/InjectionTarget.java    |  49 +++
 .../log4j/plugins/di/model/PluginModule.java       |  32 ++
 .../log4j/plugins/di/model/PluginSource.java       |  29 ++
 .../log4j/plugins/di/model/ProducerField.java      |  55 +++
 .../log4j/plugins/di/model/ProducerMethod.java     |  62 +++
 .../logging/log4j/plugins/di/spi/PluginModule.java |  54 ---
 .../log4j/plugins/processor/BeanProcessor.java     | 417 ++++++++++++++-------
 log4j-plugins/src/main/java9/module-info.java      |   6 +-
 log4j-plugins/src/test/java-test9/module-info.java |   2 +-
 .../log4j/plugins/processor/BeanProcessorTest.java |  28 +-
 log4j-plugins/src/test/java9/module-info.java      |   6 +-
 13 files changed, 635 insertions(+), 206 deletions(-)

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
new file mode 100644
index 0000000..02175cb
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/DisposesMethod.java
@@ -0,0 +1,53 @@
+/*
+ * 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.di.model;
+
+import org.apache.logging.log4j.plugins.di.Disposes;
+
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+public class DisposesMethod implements PluginSource {
+    private final String declaringClassName;
+    private final String disposesTypeName;
+
+    public DisposesMethod(final String declaringClassName, final String disposesTypeName) {
+        this.declaringClassName = declaringClassName;
+        this.disposesTypeName = disposesTypeName;
+    }
+
+    @Override
+    public String getDeclaringClassName() {
+        return declaringClassName;
+    }
+
+    public String getDisposesTypeName() {
+        return disposesTypeName;
+    }
+
+    @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
new file mode 100644
index 0000000..3bff77c
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/GenericPlugin.java
@@ -0,0 +1,48 @@
+/*
+ * 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.di.model;
+
+import org.apache.logging.log4j.plugins.Plugin;
+
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+public class GenericPlugin implements PluginSource {
+    private final String declaringClassName;
+    private final Set<Class<?>> implementedInterfaces;
+
+    public GenericPlugin(final String declaringClassName, final Set<Class<?>> implementedInterfaces) {
+        this.declaringClassName = declaringClassName;
+        this.implementedInterfaces = implementedInterfaces;
+    }
+
+    @Override
+    public String getDeclaringClassName() {
+        return declaringClassName;
+    }
+
+    @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
new file mode 100644
index 0000000..a6676e5
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/InjectionTarget.java
@@ -0,0 +1,49 @@
+/*
+ * 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.di.model;
+
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+public class InjectionTarget implements PluginSource {
+    private final String className;
+    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;
+        this.implementedInterfaces = implementedInterfaces;
+        this.scopeType = scopeType;
+    }
+
+    @Override
+    public String getDeclaringClassName() {
+        return className;
+    }
+
+    @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
new file mode 100644
index 0000000..70b7093
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginModule.java
@@ -0,0 +1,32 @@
+/*
+ * 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.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;
+    }
+}
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
new file mode 100644
index 0000000..c144f63
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginSource.java
@@ -0,0 +1,29 @@
+/*
+ * 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.di.model;
+
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+public interface PluginSource {
+    String getDeclaringClassName();
+
+    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
new file mode 100644
index 0000000..acfc542
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerField.java
@@ -0,0 +1,55 @@
+/*
+ * 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.di.model;
+
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+public class ProducerField implements PluginSource {
+    private final String declaringClassName;
+    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;
+        this.fieldName = fieldName;
+        this.implementedInterfaces = implementedInterfaces;
+        this.scopeType = scopeType;
+    }
+
+    @Override
+    public String getDeclaringClassName() {
+        return declaringClassName;
+    }
+
+    public String getFieldName() {
+        return fieldName;
+    }
+
+    @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/ProducerMethod.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerMethod.java
new file mode 100644
index 0000000..5c836b4
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerMethod.java
@@ -0,0 +1,62 @@
+/*
+ * 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.di.model;
+
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+public class ProducerMethod implements PluginSource {
+    private final String declaringClassName;
+    private final String returnTypeClassName;
+    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;
+        this.methodName = methodName;
+        this.implementedInterfaces = implementedInterfaces;
+        this.scopeType = scopeType;
+    }
+
+    @Override
+    public String getDeclaringClassName() {
+        return declaringClassName;
+    }
+
+    public String getReturnTypeClassName() {
+        return returnTypeClassName;
+    }
+
+    public String getMethodName() {
+        return methodName;
+    }
+
+    @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/spi/PluginModule.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/spi/PluginModule.java
deleted file mode 100644
index 279be74..0000000
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/spi/PluginModule.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.di.spi;
-
-import java.util.Map;
-import java.util.Set;
-
-public abstract class PluginModule {
-    private final Map<String, Set<String>> injectionBeans;
-    private final Map<String, Set<String>> producerBeans;
-    private final Set<String> destructorBeans;
-    private final Map<String, Set<String>> pluginBeans;
-
-    protected PluginModule(final Map<String, Set<String>> injectionBeans,
-                           final Map<String, Set<String>> producerBeans,
-                           final Set<String> destructorBeans,
-                           final Map<String, Set<String>> pluginBeans) {
-        this.injectionBeans = injectionBeans;
-        this.producerBeans = producerBeans;
-        this.destructorBeans = destructorBeans;
-        this.pluginBeans = pluginBeans;
-    }
-
-    public Map<String, Set<String>> getInjectionBeans() {
-        return injectionBeans;
-    }
-
-    public Map<String, Set<String>> getProducerBeans() {
-        return producerBeans;
-    }
-
-    public Set<String> getDestructorBeans() {
-        return destructorBeans;
-    }
-
-    public Map<String, Set<String>> getPluginBeans() {
-        return pluginBeans;
-    }
-}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/BeanProcessor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/BeanProcessor.java
index d2f1386..de599a2 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/BeanProcessor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/BeanProcessor.java
@@ -17,10 +17,12 @@
 
 package org.apache.logging.log4j.plugins.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;
@@ -31,31 +33,36 @@ import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ElementKind;
 import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
 import javax.lang.model.element.PackageElement;
 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.ElementKindVisitor9;
 import javax.lang.model.util.Elements;
-import javax.lang.model.util.SimpleAnnotationValueVisitor9;
+import javax.lang.model.util.SimpleTypeVisitor9;
 import javax.lang.model.util.Types;
 import javax.tools.StandardLocation;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.UncheckedIOException;
-import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.HashSet;
-import java.util.Map;
+import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Set;
-import java.util.TreeSet;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 // TODO: migrate to separate maven module between log4j-plugins and log4j-core
 @SupportedAnnotationTypes({"org.apache.logging.log4j.plugins.*", "org.apache.logging.log4j.core.config.plugins.*"})
-@SupportedOptions("pluginPackage")
+@SupportedOptions({"pluginPackage", "pluginClassName"})
 public class BeanProcessor extends AbstractProcessor {
-    public static final String PLUGIN_MODULE_SERVICE_FILE = "META-INF/services/org.apache.logging.log4j.plugins.di.spi.PluginModule";
+    public static final String PLUGIN_MODULE_SERVICE_FILE = "META-INF/services/org.apache.logging.log4j.plugins.di.model.PluginModule";
 
     public BeanProcessor() {
     }
@@ -66,57 +73,57 @@ public class BeanProcessor extends AbstractProcessor {
     }
 
     private static class ProducerAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
-        private final Set<ExecutableElement> producerMethods = new HashSet<>();
-        private final Set<VariableElement> producerFields = new HashSet<>();
+        private final List<ProducerMethodMirror> producerMethods = new ArrayList<>();
+        private final List<ProducerFieldMirror> producerFields = new ArrayList<>();
 
         @Override
         public Void visitVariableAsField(final VariableElement e, final Void unused) {
-            producerFields.add(e);
+            producerFields.add(new ProducerFieldMirror(e));
             return null;
         }
 
         @Override
         public Void visitExecutableAsMethod(final ExecutableElement e, final Void unused) {
-            producerMethods.add(e);
+            producerMethods.add(new ProducerMethodMirror(e));
             return null;
         }
     }
 
     private static class DisposesAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
-        private final Set<ExecutableElement> disposesMethods = new HashSet<>();
+        private final List<DisposesMirror> disposesParameters = new ArrayList<>();
 
         @Override
         public Void visitVariableAsParameter(final VariableElement e, final Void unused) {
-            disposesMethods.add((ExecutableElement) e.getEnclosingElement());
+            disposesParameters.add(new DisposesMirror(e));
             return null;
         }
     }
 
     private static class InjectAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
-        private final Set<TypeElement> injectableClasses = new HashSet<>();
+        private final List<InjectionTargetMirror> injectableClasses = new ArrayList<>();
 
         @Override
         public Void visitVariableAsField(final VariableElement e, final Void unused) {
-            injectableClasses.add((TypeElement) e.getEnclosingElement());
+            injectableClasses.add(new InjectionTargetMirror(((TypeElement) e.getEnclosingElement())));
             return null;
         }
 
         @Override
         public Void visitExecutableAsConstructor(final ExecutableElement e, final Void unused) {
-            injectableClasses.add((TypeElement) e.getEnclosingElement());
+            injectableClasses.add(new InjectionTargetMirror((TypeElement) e.getEnclosingElement()));
             return null;
         }
 
         @Override
         public Void visitExecutableAsMethod(final ExecutableElement e, final Void unused) {
-            injectableClasses.add((TypeElement) e.getEnclosingElement());
+            injectableClasses.add(new InjectionTargetMirror((TypeElement) e.getEnclosingElement()));
             return null;
         }
     }
 
     private static class QualifiedAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
         private final Predicate<AnnotationMirror> isProducerAnnotation;
-        private final Set<TypeElement> injectableClasses = new HashSet<>();
+        private final List<InjectionTargetMirror> injectableClasses = new ArrayList<>();
 
         private QualifiedAnnotationVisitor(final Predicate<AnnotationMirror> isProducerAnnotation) {
             this.isProducerAnnotation = isProducerAnnotation;
@@ -125,7 +132,7 @@ public class BeanProcessor extends AbstractProcessor {
         @Override
         public Void visitVariableAsField(final VariableElement e, final Void unused) {
             if (e.getAnnotationMirrors().stream().noneMatch(isProducerAnnotation)) {
-                injectableClasses.add((TypeElement) e.getEnclosingElement());
+                injectableClasses.add(new InjectionTargetMirror((TypeElement) e.getEnclosingElement()));
             }
             return null;
         }
@@ -136,43 +143,182 @@ public class BeanProcessor extends AbstractProcessor {
             final TypeElement typeElement = (TypeElement) enclosingExecutable.getEnclosingElement();
             if (enclosingExecutable.getKind() == ElementKind.CONSTRUCTOR ||
                     enclosingExecutable.getAnnotationMirrors().stream().noneMatch(isProducerAnnotation)) {
-                injectableClasses.add(typeElement);
+                injectableClasses.add(new InjectionTargetMirror(typeElement));
             }
             return null;
         }
     }
 
     private static class PluginAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
-        private final Map<String, Set<TypeElement>> pluginCategories = new HashMap<>();
+        private final List<GenericPluginMirror> plugins = new ArrayList<>();
 
         @Override
         public Void visitTypeAsClass(final TypeElement e, final Void unused) {
-            final AnnotationMirror pluginAnnotation = e.getAnnotationMirrors()
-                    .stream()
-                    .filter(ann -> ann.getAnnotationType().asElement().getSimpleName().contentEquals("Plugin"))
-                    .findAny()
-                    .orElseThrow();
-            final ExecutableElement categoryKey = pluginAnnotation.getElementValues()
-                    .keySet()
-                    .stream()
-                    .filter(element -> element.getSimpleName().contentEquals("category"))
-                    .findAny()
-                    .orElseThrow();
-
-            final String category = pluginAnnotation.getElementValues()
-                    .get(categoryKey)
-                    .accept(new SimpleAnnotationValueVisitor9<String, Void>() {
-                        @Override
-                        public String visitString(final String s, final Void unused1) {
-                            return s;
-                        }
-                    }, null);
-            pluginCategories.computeIfAbsent(category, ignored -> new HashSet<>()).add(e);
-
+            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();
+                }
+            }
+            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);
+        }
+    }
+
+    interface PluginSourceMirror<E extends Element> {
+        E getElement();
+
+        TypeElement getDeclaringElement();
+
+        TypeMirror getType();
+    }
+
+    static class ProducerMethodMirror implements PluginSourceMirror<ExecutableElement> {
+        private final ExecutableElement element;
+
+        ProducerMethodMirror(final ExecutableElement element) {
+            this.element = element;
+        }
+
+        @Override
+        public ExecutableElement getElement() {
+            return element;
+        }
+
+        @Override
+        public TypeElement getDeclaringElement() {
+            return (TypeElement) element.getEnclosingElement();
+        }
+
+        @Override
+        public TypeMirror getType() {
+            return element.getReturnType();
+        }
+    }
+
+    static class ProducerFieldMirror implements PluginSourceMirror<VariableElement> {
+        private final VariableElement element;
+
+        ProducerFieldMirror(final VariableElement element) {
+            this.element = element;
+        }
+
+        @Override
+        public VariableElement getElement() {
+            return element;
+        }
+
+        @Override
+        public TypeElement getDeclaringElement() {
+            return (TypeElement) element.getEnclosingElement();
+        }
+
+        @Override
+        public TypeMirror getType() {
+            return element.asType();
+        }
+    }
+
+    static class InjectionTargetMirror implements PluginSourceMirror<TypeElement> {
+        private final TypeElement element;
+
+        InjectionTargetMirror(final TypeElement element) {
+            this.element = element;
+        }
+
+        @Override
+        public TypeElement getElement() {
+            return element;
+        }
+
+        @Override
+        public TypeElement getDeclaringElement() {
+            return element;
+        }
+
+        @Override
+        public TypeMirror getType() {
+            return element.asType();
+        }
+    }
+
+    static class DisposesMirror implements PluginSourceMirror<VariableElement> {
+        private final VariableElement element;
+
+        DisposesMirror(final VariableElement element) {
+            this.element = element;
+        }
+
+        @Override
+        public VariableElement getElement() {
+            return element;
+        }
+
+        @Override
+        public TypeElement getDeclaringElement() {
+            return (TypeElement) element.getEnclosingElement().getEnclosingElement();
+        }
+
+        @Override
+        public TypeMirror getType() {
+            return element.asType();
+        }
+    }
+
+    static class GenericPluginMirror implements PluginSourceMirror<TypeElement> {
+        private final TypeElement element;
+
+        GenericPluginMirror(final TypeElement element) {
+            this.element = element;
+        }
+
+        @Override
+        public TypeElement getElement() {
+            return element;
+        }
+
+        @Override
+        public TypeElement getDeclaringElement() {
+            return element;
+        }
+
+        @Override
+        public TypeMirror getType() {
+            return element.asType();
+        }
+    }
+
     @Override
     public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
         if (annotations.isEmpty()) {
@@ -212,49 +358,33 @@ public class BeanProcessor extends AbstractProcessor {
         roundEnv.getElementsAnnotatedWithAny(pluginAnnotations).forEach(pluginAnnotationVisitor::visit);
 
         final Set<PackageElement> packageElements = new HashSet<>();
+        final Set<TypeElement> declaringTypes = new HashSet<>();
 
         final Elements elements = processingEnv.getElementUtils();
-        final Map<String, Set<String>> producerBeans = new HashMap<>();
-        for (final ExecutableElement producerMethod : producesAnnotationVisitor.producerMethods) {
-            final TypeElement declaringType = (TypeElement) producerMethod.getEnclosingElement();
-            packageElements.add(elements.getPackageOf(declaringType));
-            final String declaringClassName = elements.getBinaryName(declaringType).toString();
-            final Set<String> classNameHierarchy = getClassNameHierarchy(producerMethod.getReturnType());
-            producerBeans.put(declaringClassName, classNameHierarchy);
-        }
-        for (final VariableElement producerField : producesAnnotationVisitor.producerFields) {
-            final TypeElement declaringType = (TypeElement) producerField.getEnclosingElement();
-            packageElements.add(elements.getPackageOf(declaringType));
-            final String declaringClassName = elements.getBinaryName(declaringType).toString();
-            final Set<String> classNameHierarchy = getClassNameHierarchy(producerField.asType());
-            producerBeans.put(declaringClassName, classNameHierarchy);
-        }
-
-        final Set<CharSequence> destructibleClassNames = disposesAnnotationVisitor.disposesMethods.stream()
-                .map(e -> (TypeElement) e.getEnclosingElement())
-                .peek(e -> packageElements.add(elements.getPackageOf(e)))
-                .map(elements::getBinaryName)
-                .collect(Collectors.toSet());
-
-        final Map<String, Set<String>> injectionBeans = new HashMap<>();
-        final Set<TypeElement> injectionClasses = new HashSet<>(injectAnnotationVisitor.injectableClasses);
-        injectionClasses.addAll(qualifiedAnnotationVisitor.injectableClasses);
-        for (final TypeElement injectableClass : injectionClasses) {
-            packageElements.add(elements.getPackageOf(injectableClass));
-            final String declaringClassName = elements.getBinaryName(injectableClass).toString();
-            final Set<String> classNameHierarchy = getClassNameHierarchy(injectableClass.asType());
-            injectionBeans.put(declaringClassName, classNameHierarchy);
-        }
-
-        final Map<String, Set<String>> pluginBeans = pluginAnnotationVisitor.pluginCategories
-                .values()
-                .stream()
-                .flatMap(Set::stream)
-                .peek(typeElement -> packageElements.add(elements.getPackageOf(typeElement)))
-                .collect(Collectors.toMap(
-                        typeElement -> elements.getBinaryName(typeElement).toString(),
-                        typeElement -> getClassNameHierarchy(typeElement.asType())
-                ));
+        final List<PluginSourceMirror<?>> mirrors = new ArrayList<>(producesAnnotationVisitor.producerMethods);
+        mirrors.addAll(producesAnnotationVisitor.producerFields);
+        mirrors.addAll(injectAnnotationVisitor.injectableClasses);
+        mirrors.addAll(disposesAnnotationVisitor.disposesParameters);
+        mirrors.forEach(mirror -> {
+            declaringTypes.add(mirror.getDeclaringElement());
+            packageElements.add(elements.getPackageOf(mirror.getDeclaringElement()));
+        });
+
+        qualifiedAnnotationVisitor.injectableClasses.stream()
+                .filter(mirror -> !declaringTypes.contains(mirror.getDeclaringElement()))
+                .forEach(mirror -> {
+                    mirrors.add(mirror);
+                    declaringTypes.add(mirror.getDeclaringElement());
+                    packageElements.add(elements.getPackageOf(mirror.getDeclaringElement()));
+                });
+
+        pluginAnnotationVisitor.plugins.stream()
+                .filter(mirror -> !declaringTypes.contains(mirror.getDeclaringElement()))
+                .forEach(mirror -> {
+                    mirrors.add(mirror);
+                    declaringTypes.add(mirror.getDeclaringElement());
+                    packageElements.add(elements.getPackageOf(mirror.getDeclaringElement()));
+                });
 
         String packageName = processingEnv.getOptions().get("pluginPackage");
         if (packageName == null) {
@@ -267,7 +397,7 @@ public class BeanProcessor extends AbstractProcessor {
         }
         String className = processingEnv.getOptions().getOrDefault("pluginClassName", "Log4jModule");
         try {
-            writePluginModule(packageName, className, injectionBeans, producerBeans, destructibleClassNames, pluginBeans);
+            writePluginModule(packageName, className, mirrors);
             return false;
         } catch (IOException e) {
             throw new UncheckedIOException(e);
@@ -275,10 +405,7 @@ public class BeanProcessor extends AbstractProcessor {
     }
 
     private void writePluginModule(final CharSequence packageName, final CharSequence className,
-                                   final Map<String, Set<String>> injectionBeans,
-                                   final Map<String, Set<String>> producerBeans,
-                                   final Set<CharSequence> destructorBeans,
-                                   final Map<String, Set<String>> pluginBeans) throws IOException {
+                                   final List<PluginSourceMirror<?>> mirrors) throws IOException {
         try (final PrintWriter out = new PrintWriter(processingEnv.getFiler().createResource(
                 StandardLocation.CLASS_OUTPUT, "", PLUGIN_MODULE_SERVICE_FILE).openWriter())) {
             out.println(packageName + ".plugins." + className);
@@ -287,75 +414,91 @@ public class BeanProcessor extends AbstractProcessor {
                 packageName + ".plugins." + className).openWriter())) {
             out.println("package " + packageName + ".plugins;");
             out.println();
-            out.println("import java.util.*;");
+            out.println("import org.apache.logging.log4j.plugins.di.model.*;");
             out.println();
-            out.println("import org.apache.logging.log4j.plugins.di.spi.PluginModule;");
+            out.println("import java.util.List;");
+            out.println("import java.util.Set;");
             out.println();
             out.println("@javax.annotation.processing.Generated(\"" + getClass().getName() + "\")");
             out.println("public class " + className + " extends PluginModule {");
             out.println();
-            out.println("  private static final Map<String, Set<String>> INJECT =");
-            out.println("    new TreeMap<>(Map.ofEntries(" + getEntries(injectionBeans) + "));");
-            out.println();
-            out.println("  private static final Map<String, Set<String>> PRODUCE =");
-            out.println("    new TreeMap<>(Map.ofEntries(" + getEntries(producerBeans) + "));");
-            out.println();
-            out.println("  private static final Set<String> DESTROY = new TreeSet<>(Set.of(" + destructorBeans.stream().sorted(CharSequence::compare).collect(Collectors.joining("\",\n\"", "\n\"", "\"\n")) + "));");
-            out.println();
-            out.println("  private static final Map<String, Set<String>> PLUGINS =");
-            out.println("    new TreeMap<>(Map.ofEntries(" + getEntries(pluginBeans) + "));");
+            out.println("  private static final List<PluginSource> PLUGINS = List.of(" + javaListOfPlugins(mirrors) + ");");
             out.println();
             out.println("  public " + className + "() {");
-            out.println("    super(INJECT, PRODUCE, DESTROY, PLUGINS);");
+            out.println("    super(PLUGINS);");
             out.println("  }");
             out.println();
             out.println("}");
         }
     }
 
-    private static String getEntries(final Map<String, Set<String>> beans) {
-        return beans.entrySet().stream()
-                .sorted(Map.Entry.comparingByKey())
-                .map(e -> "Map.entry(\"" + e.getKey() + "\", new TreeSet<>(Set.of(" + e.getValue().stream().sorted().collect(Collectors.joining("\", \"", "\"", "\"")) + ")))")
+    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 + ", \"" +
+                                mirror.getType().toString() + "\", \"" +
+                                mirror.getElement().getSimpleName() + "\", " +
+                                setOfImplementedInterfaces + ", " +
+                                scopeTypeClassReference + ")";
+                    } else if (mirror instanceof ProducerFieldMirror) {
+                        return "new ProducerField(" + declaringClassName + ", \"" +
+                                mirror.getElement().getSimpleName() + "\", " +
+                                setOfImplementedInterfaces + ", " +
+                                scopeTypeClassReference + ")";
+                    } else if (mirror instanceof InjectionTargetMirror) {
+                        return "new InjectionTarget(" + declaringClassName + ", " +
+                                setOfImplementedInterfaces + ", " +
+                                scopeTypeClassReference + ")";
+                    } else if (mirror instanceof DisposesMirror) {
+                        return "new DisposesMethod(" + declaringClassName + ", \"" +
+                                elements.getBinaryName((TypeElement) types.asElement(mirror.getElement().asType())) + "\")";
+                    } else if (mirror instanceof GenericPluginMirror) {
+                        return "new GenericPlugin(" + declaringClassName + ", " + setOfImplementedInterfaces + ")";
+                    } else {
+                        throw new UnsupportedOperationException(mirror.getClass().getName());
+                    }
+                })
                 .collect(Collectors.joining(",\n", "\n", "\n"));
     }
 
-    private Set<String> getClassNameHierarchy(final TypeMirror base) {
-        final Set<String> hierarchy = new TreeSet<>();
-        walkClassHierarchy(base, hierarchy);
-        return hierarchy;
+    private String javaSetOfImplementedInterfaces(final TypeMirror base) {
+        final Set<Name> implementedInterfaces = getImplementedInterfaces(base);
+        return implementedInterfaces.isEmpty() ? "Set.of()" : "Set.of(" +
+                implementedInterfaces.stream().map(name -> name + ".class").collect(Collectors.joining(", ")) +
+                ")";
     }
 
-    private void walkClassHierarchy(final TypeMirror base, final Set<String> encountered) {
+    private Set<Name> getImplementedInterfaces(final TypeMirror base) {
+        final Set<Name> implementedInterfaces = new LinkedHashSet<>();
         final Types types = processingEnv.getTypeUtils();
-        final Elements elements = processingEnv.getElementUtils();
-        final Element element = types.asElement(base);
-        if (element != null) {
-            element.accept(new ElementKindVisitor9<Void, Void>() {
-                @Override
-                public Void visitTypeAsClass(final TypeElement e, final Void unused) {
-                    if (!elements.getPackageOf(e).getQualifiedName().contentEquals("java.lang")) {
-                        encountered.add(elements.getBinaryName(e).toString());
-                    }
-                    return null;
+        base.accept(new SimpleTypeVisitor9<Void, Void>() {
+            @Override
+            public Void visitDeclared(final DeclaredType t, final Void unused) {
+                for (final TypeMirror directSupertype : types.directSupertypes(t)) {
+                    directSupertype.accept(this, null);
                 }
-
-                @Override
-                public Void visitTypeAsEnum(final TypeElement e, final Void unused) {
-                    encountered.add(elements.getBinaryName(e).toString());
-                    return null;
-                }
-
-                @Override
-                public Void visitTypeAsInterface(final TypeElement e, final Void unused) {
-                    encountered.add(elements.getBinaryName(e).toString());
-                    return null;
-                }
-            }, null);
-        }
-        for (final TypeMirror directSupertype : types.directSupertypes(base)) {
-            walkClassHierarchy(directSupertype, encountered);
-        }
+                t.asElement().accept(new ElementKindVisitor9<Void, Void>() {
+                    @Override
+                    public Void visitTypeAsInterface(final TypeElement e, final Void unused) {
+                        if (e.getModifiers().contains(Modifier.PUBLIC)) {
+                            implementedInterfaces.add(e.getQualifiedName());
+                        }
+                        return null;
+                    }
+                }, null);
+                return null;
+            }
+        }, null);
+        return implementedInterfaces;
     }
 
     private static CharSequence commonPrefix(final CharSequence str1, final CharSequence str2) {
diff --git a/log4j-plugins/src/main/java9/module-info.java b/log4j-plugins/src/main/java9/module-info.java
index 01e587d..daee9e0 100644
--- a/log4j-plugins/src/main/java9/module-info.java
+++ b/log4j-plugins/src/main/java9/module-info.java
@@ -18,7 +18,7 @@ module org.apache.logging.log4j.plugins {
     exports org.apache.logging.log4j.plugins;
     exports org.apache.logging.log4j.plugins.convert;
     exports org.apache.logging.log4j.plugins.di;
-    exports org.apache.logging.log4j.plugins.di.spi;
+    exports org.apache.logging.log4j.plugins.di.model;
     exports org.apache.logging.log4j.plugins.processor;
     exports org.apache.logging.log4j.plugins.util;
     exports org.apache.logging.log4j.plugins.validation;
@@ -34,12 +34,12 @@ module org.apache.logging.log4j.plugins {
 
     provides org.apache.logging.log4j.plugins.processor.PluginService with
             org.apache.logging.log4j.plugins.convert.plugins.Log4jPlugins;
-    provides org.apache.logging.log4j.plugins.di.spi.PluginModule with
+    provides org.apache.logging.log4j.plugins.di.model.PluginModule with
             org.apache.logging.log4j.plugins.convert.plugins.Log4jModule;
     provides javax.annotation.processing.Processor with
             org.apache.logging.log4j.plugins.processor.PluginProcessor,
             org.apache.logging.log4j.plugins.processor.BeanProcessor;
 
     uses org.apache.logging.log4j.plugins.processor.PluginService;
-//    uses org.apache.logging.log4j.plugins.di.spi.PluginModule;
+//    uses org.apache.logging.log4j.plugins.di.model.PluginModule;
 }
diff --git a/log4j-plugins/src/test/java-test9/module-info.java b/log4j-plugins/src/test/java-test9/module-info.java
index 3ac8a6a..1975b79 100644
--- a/log4j-plugins/src/test/java-test9/module-info.java
+++ b/log4j-plugins/src/test/java-test9/module-info.java
@@ -6,6 +6,6 @@ module org.apache.logging.log4j.plugins.test {
 
     provides org.apache.logging.log4j.plugins.processor.PluginService with
             org.apache.logging.log4j.plugins.test.validation.plugins.Log4jPlugins;
-    provides org.apache.logging.log4j.plugins.di.spi.PluginModule with
+    provides org.apache.logging.log4j.plugins.di.model.PluginModule with
             org.apache.logging.log4j.plugins.test.validation.plugins.Log4jModule;
 }
diff --git a/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/BeanProcessorTest.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/BeanProcessorTest.java
index 7afe944..b699b4a 100644
--- a/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/BeanProcessorTest.java
+++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/BeanProcessorTest.java
@@ -17,7 +17,11 @@
 
 package org.apache.logging.log4j.plugins.processor;
 
-import org.apache.logging.log4j.plugins.di.spi.PluginModule;
+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.junit.jupiter.api.Test;
 
 import java.util.ServiceLoader;
@@ -30,12 +34,20 @@ class BeanProcessorTest {
         final PluginModule service = ServiceLoader.load(PluginModule.class, getClass().getClassLoader())
                 .findFirst()
                 .orElseThrow();
-        assertTrue(service.getInjectionBeans().containsKey("org.apache.logging.log4j.plugins.test.validation.ExampleBean"));
-        assertTrue(service.getInjectionBeans().containsKey("org.apache.logging.log4j.plugins.test.validation.ImplicitBean"));
-        assertTrue(service.getInjectionBeans().containsKey("org.apache.logging.log4j.plugins.test.validation.ImplicitMethodBean"));
-        assertTrue(service.getInjectionBeans().containsKey("org.apache.logging.log4j.plugins.test.validation.ProductionBean$Builder"));
-        assertTrue(service.getProducerBeans().containsKey("org.apache.logging.log4j.plugins.test.validation.ProductionBean"));
-        assertTrue(service.getDestructorBeans().contains("org.apache.logging.log4j.plugins.test.validation.ProductionBean"));
-        assertTrue(service.getPluginBeans().containsKey(FakePlugin.class.getName()));
+        final var plugins = service.getPluginSources();
+        assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof InjectionTarget &&
+                plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ExampleBean")));
+        assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof InjectionTarget &&
+                plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ImplicitBean")));
+        assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof InjectionTarget &&
+                plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ImplicitMethodBean")));
+        assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof InjectionTarget &&
+                plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ProductionBean$Builder")));
+        assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof ProducerField &&
+                plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ProductionBean")));
+        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())));
     }
 }
\ No newline at end of file
diff --git a/log4j-plugins/src/test/java9/module-info.java b/log4j-plugins/src/test/java9/module-info.java
index 4195946..99357b5 100644
--- a/log4j-plugins/src/test/java9/module-info.java
+++ b/log4j-plugins/src/test/java9/module-info.java
@@ -2,7 +2,7 @@ open module org.apache.logging.log4j.plugins {
     exports org.apache.logging.log4j.plugins;
     exports org.apache.logging.log4j.plugins.convert;
     exports org.apache.logging.log4j.plugins.di;
-    exports org.apache.logging.log4j.plugins.di.spi;
+    exports org.apache.logging.log4j.plugins.di.model;
     exports org.apache.logging.log4j.plugins.name;
     exports org.apache.logging.log4j.plugins.processor;
     exports org.apache.logging.log4j.plugins.util;
@@ -25,12 +25,12 @@ open module org.apache.logging.log4j.plugins {
 
     provides org.apache.logging.log4j.plugins.processor.PluginService with
             org.apache.logging.log4j.plugins.convert.plugins.Log4jPlugins;
-    provides org.apache.logging.log4j.plugins.di.spi.PluginModule with
+    provides org.apache.logging.log4j.plugins.di.model.PluginModule with
             org.apache.logging.log4j.plugins.convert.plugins.Log4jModule;
     provides javax.annotation.processing.Processor with
             org.apache.logging.log4j.plugins.processor.PluginProcessor,
             org.apache.logging.log4j.plugins.processor.BeanProcessor;
 
     uses org.apache.logging.log4j.plugins.processor.PluginService;
-    uses org.apache.logging.log4j.plugins.di.spi.PluginModule;
+    uses org.apache.logging.log4j.plugins.di.model.PluginModule;
 }

[logging-log4j2] 02/05: Add type hierarchy data to plugin modules

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

mattsicker pushed a commit to branch mean-bean-machine
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit c3c88f52448f047d7535186ce4e226d4ea6dff7a
Author: Matt Sicker <bo...@gmail.com>
AuthorDate: Sat Jul 24 18:59:14 2021 -0500

    Add type hierarchy data to plugin modules
    
    This also renames BeanInfoService to PluginModule along with some pom simplifications discovered while debugging MCOMPILER-461.
---
 log4j-core/pom.xml                                 |  35 +---
 log4j-core/src/main/java9/module-info.java         |  12 +-
 log4j-plugins/pom.xml                              |  80 +--------
 .../log4j/plugins/di/spi/BeanInfoService.java      |  52 ------
 .../logging/log4j/plugins/di/spi/PluginModule.java |  54 +++++++
 .../log4j/plugins/processor/BeanProcessor.java     | 179 ++++++++++++---------
 log4j-plugins/src/main/java9/module-info.java      |  12 +-
 log4j-plugins/src/test/java-test9/module-info.java |   5 +-
 .../log4j/plugins/processor/BeanProcessorTest.java |  31 ++--
 log4j-plugins/src/test/java9/module-info.java      |  12 +-
 pom.xml                                            |   2 +-
 11 files changed, 203 insertions(+), 271 deletions(-)

diff --git a/log4j-core/pom.xml b/log4j-core/pom.xml
index 8402405..31700a0 100644
--- a/log4j-core/pom.xml
+++ b/log4j-core/pom.xml
@@ -330,7 +330,7 @@
                   <includes>
                     <include>module-info.class</include>
                     <include>**/Log4jPlugins.class</include>
-                    <include>**/Log4jBeanInfo.class</include>
+                    <include>**/Log4jModule.class</include>
                   </includes>
                 </fileset>
                 <fileset>
@@ -353,13 +353,6 @@
               <goal>compile</goal>
             </goals>
             <phase>compile</phase>
-            <configuration combine.self="override">
-              <source>${maven.compiler.source}</source>
-              <target>${maven.compiler.target}</target>
-              <release>${maven.compiler.release}</release>
-              <forceJavacCompilerUse>true</forceJavacCompilerUse>
-              <parameters>true</parameters>
-            </configuration>
           </execution>
           <execution>
             <!-- compile the module-info.java -->
@@ -369,11 +362,6 @@
             </goals>
             <phase>compile</phase>
             <configuration combine.self="override">
-              <source>${maven.compiler.source}</source>
-              <target>${maven.compiler.target}</target>
-              <release>${maven.compiler.release}</release>
-              <forceJavacCompilerUse>true</forceJavacCompilerUse>
-              <parameters>true</parameters>
               <compileSourceRoots>
                 <compileSourceRoot>${project.basedir}/src/main/java9</compileSourceRoot>
               </compileSourceRoots>
@@ -387,15 +375,10 @@
             </goals>
             <phase>compile</phase>
             <configuration>
-              <source>${maven.compiler.source}</source>
-              <target>${maven.compiler.target}</target>
-              <release>${maven.compiler.release}</release>
               <compileSourceRoots>
                 <compileSourceRoot>${project.basedir}/src/test/java-test</compileSourceRoot>
                 <compileSourceRoot>${project.basedir}/src/test/java-test9</compileSourceRoot>
               </compileSourceRoots>
-              <forceJavacCompilerUse>true</forceJavacCompilerUse>
-              <parameters>true</parameters>
             </configuration>
           </execution>
           <execution>
@@ -406,14 +389,9 @@
             </goals>
             <phase>process-test-sources</phase>
             <configuration>
-              <source>${maven.compiler.source}</source>
-              <target>${maven.compiler.target}</target>
-              <release>${maven.compiler.release}</release>
-              <forceJavacCompilerUse>true</forceJavacCompilerUse>
-              <parameters>true</parameters>
-              <compilerArgs>
-                <arg>-ApluginPackage=org.apache.logging.log4j.core.test</arg>
-              </compilerArgs>
+              <compilerArguments>
+                <ApluginPackage>org.apache.logging.log4j.core.test</ApluginPackage>
+              </compilerArguments>
               <compileSourceRoots>
                 <compileSourceRoot>${project.basedir}/src/test/java</compileSourceRoot>
                 <compileSourceRoot>${project.basedir}/src/test/java-test</compileSourceRoot>
@@ -427,11 +405,6 @@
               <goal>testCompile</goal>
             </goals>
             <configuration>
-              <source>${maven.compiler.source}</source>
-              <target>${maven.compiler.target}</target>
-              <release>${maven.compiler.release}</release>
-              <forceJavacCompilerUse>true</forceJavacCompilerUse>
-              <parameters>true</parameters>
               <compileSourceRoots>
                 <compileSourceRoot>${project.basedir}/src/test/java9</compileSourceRoot>
               </compileSourceRoots>
diff --git a/log4j-core/src/main/java9/module-info.java b/log4j-core/src/main/java9/module-info.java
index d4b3d93..890f348 100644
--- a/log4j-core/src/main/java9/module-info.java
+++ b/log4j-core/src/main/java9/module-info.java
@@ -81,8 +81,12 @@ module org.apache.logging.log4j.core {
     requires transitive org.fusesource.jansi;
     uses org.apache.logging.log4j.core.util.ContextDataProvider;
     uses org.apache.logging.log4j.core.util.WatchEventService;
-    provides org.apache.logging.log4j.message.ThreadDumpMessage.ThreadInfoFactory with org.apache.logging.log4j.core.message.ExtendedThreadInfoFactory;
-    provides org.apache.logging.log4j.core.util.ContextDataProvider with org.apache.logging.log4j.core.impl.ThreadContextDataProvider;
-    provides org.apache.logging.log4j.spi.Provider with org.apache.logging.log4j.core.impl.Log4jProvider;
-    provides org.apache.logging.log4j.plugins.processor.PluginService with org.apache.logging.log4j.core.plugins.Log4jPlugins;
+    provides org.apache.logging.log4j.message.ThreadDumpMessage.ThreadInfoFactory with
+            org.apache.logging.log4j.core.message.ExtendedThreadInfoFactory;
+    provides org.apache.logging.log4j.core.util.ContextDataProvider with
+            org.apache.logging.log4j.core.impl.ThreadContextDataProvider;
+    provides org.apache.logging.log4j.spi.Provider with
+            org.apache.logging.log4j.core.impl.Log4jProvider;
+    provides org.apache.logging.log4j.plugins.processor.PluginService with
+            org.apache.logging.log4j.core.plugins.Log4jPlugins;
 }
diff --git a/log4j-plugins/pom.xml b/log4j-plugins/pom.xml
index c562089..0bd5930 100644
--- a/log4j-plugins/pom.xml
+++ b/log4j-plugins/pom.xml
@@ -105,7 +105,7 @@
                   <includes>
                     <include>module-info.class</include>
                     <include>**/Log4jPlugins.class</include>
-                    <include>**/Log4jBeanInfo.class</include>
+                    <include>**/Log4jModule.class</include>
                   </includes>
                 </fileset>
                 <fileset>
@@ -140,14 +140,11 @@
             </goals>
             <phase>generate-sources</phase>
             <configuration combine.self="override">
-              <source>${maven.compiler.source}</source>
-              <target>${maven.compiler.target}</target>
               <proc>only</proc>
               <annotationProcessors>
                 <annotationProcessor>org.apache.logging.log4j.plugins.processor.PluginProcessor</annotationProcessor>
                 <annotationProcessor>org.apache.logging.log4j.plugins.processor.BeanProcessor</annotationProcessor>
               </annotationProcessors>
-              <parameters>true</parameters>
             </configuration>
           </execution>
           <execution>
@@ -158,8 +155,6 @@
             </goals>
             <phase>generate-sources</phase>
             <configuration combine.self="override">
-              <source>${maven.compiler.source}</source>
-              <target>${maven.compiler.target}</target>
               <proc>only</proc>
               <annotationProcessors>
                 <annotationProcessor>org.apache.logging.log4j.plugins.processor.PluginProcessor</annotationProcessor>
@@ -168,7 +163,6 @@
               <compileSourceRoots>
                 <compileSourceRoot>${project.basedir}/src/test/java-test</compileSourceRoot>
               </compileSourceRoots>
-              <parameters>true</parameters>
             </configuration>
           </execution>
           <execution>
@@ -179,8 +173,6 @@
             </goals>
             <phase>generate-sources</phase>
             <configuration combine.self="override">
-              <source>${maven.compiler.source}</source>
-              <target>${maven.compiler.target}</target>
               <proc>only</proc>
               <annotationProcessors>
                 <annotationProcessor>org.apache.logging.log4j.plugins.processor.PluginProcessor</annotationProcessor>
@@ -195,7 +187,6 @@
               </compileSourceRoots>
               <generatedTestSourcesDirectory>${project.build.directory}/generated-test-sources/unit-test-annotations</generatedTestSourcesDirectory>
               <outputDirectory>${project.build.directory}/unit-test-classes</outputDirectory>
-              <parameters>true</parameters>
             </configuration>
           </execution>
           <execution>
@@ -205,37 +196,6 @@
               <goal>compile</goal>
             </goals>
             <configuration combine.self="override">
-              <source>${maven.compiler.source}</source>
-              <target>${maven.compiler.target}</target>
-              <release>${maven.compiler.release}</release>
-              <showDeprecation>true</showDeprecation>
-              <showWarnings>true</showWarnings>
-              <encoding>UTF-8</encoding>
-              <fork>true</fork>
-              <meminitial>256</meminitial>
-              <maxmem>1024</maxmem>
-              <compilerArgs>
-                <arg>-XDcompilePolicy=simple</arg>
-                <arg>-Xplugin:ErrorProne</arg>
-                <!--
-                https://errorprone.info/docs/installation
-                in Java 16+, https://openjdk.java.net/jeps/396 encapsulates internals that errorprone needs
-                -->
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
-                <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
-                <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
-              </compilerArgs>
-              <compilerArguments>
-                <Xmaxwarns>10000</Xmaxwarns>
-                <Xlint />
-              </compilerArguments>
               <annotationProcessorPaths>
                 <path>
                   <groupId>com.google.errorprone</groupId>
@@ -243,8 +203,6 @@
                   <version>${errorprone.version}</version>
                 </path>
               </annotationProcessorPaths>
-              <forceJavacCompilerUse>true</forceJavacCompilerUse>
-              <parameters>true</parameters>
               <compileSourceRoots>
                 <compileSourceRoot>${project.basedir}/src/main/java</compileSourceRoot>
                 <compileSourceRoot>${project.basedir}/src/main/java9</compileSourceRoot>
@@ -259,14 +217,11 @@
             </goals>
             <phase>process-classes</phase>
             <configuration combine.self="override">
-              <source>${maven.compiler.source}</source>
-              <target>${maven.compiler.target}</target>
               <proc>none</proc>
               <compileSourceRoots>
                 <compileSourceRoot>${project.basedir}/src/test/java-test</compileSourceRoot>
                 <compileSourceRoot>${project.basedir}/src/test/java-test9</compileSourceRoot>
               </compileSourceRoots>
-              <parameters>true</parameters>
             </configuration>
           </execution>
           <execution>
@@ -276,39 +231,6 @@
               <goal>testCompile</goal>
             </goals>
             <configuration combine.self="override">
-              <source>${maven.compiler.source}</source>
-              <target>${maven.compiler.target}</target>
-              <release>${maven.compiler.release}</release>
-              <showDeprecation>true</showDeprecation>
-              <showWarnings>true</showWarnings>
-              <encoding>UTF-8</encoding>
-              <fork>true</fork>
-              <meminitial>256</meminitial>
-              <maxmem>1024</maxmem>
-              <compilerArgs>
-                <arg>-XDcompilePolicy=simple</arg>
-                <arg>-Xplugin:ErrorProne</arg>
-                <!--
-                https://errorprone.info/docs/installation
-                in Java 16+, https://openjdk.java.net/jeps/396 encapsulates internals that errorprone needs
-                -->
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
-                <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
-                <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
-                <arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
-              </compilerArgs>
-              <compilerArguments>
-                <Xmaxwarns>10000</Xmaxwarns>
-                <Xlint />
-              </compilerArguments>
-              <forceJavacCompilerUse>true</forceJavacCompilerUse>
-              <parameters>true</parameters>
               <annotationProcessorPaths>
                 <path>
                   <groupId>com.google.errorprone</groupId>
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/spi/BeanInfoService.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/spi/BeanInfoService.java
deleted file mode 100644
index f7f45e1..0000000
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/spi/BeanInfoService.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.di.spi;
-
-import java.util.List;
-import java.util.Map;
-
-public abstract class BeanInfoService {
-    private final List<String> injectableClassNames;
-    private final List<String> producibleClassNames;
-    private final List<String> destructibleClassNames;
-    private final Map<String, List<String>> pluginCategories;
-
-    protected BeanInfoService(final List<String> injectableClassNames, final List<String> producibleClassNames,
-                              final List<String> destructibleClassNames, final Map<String, List<String>> pluginCategories) {
-        this.injectableClassNames = injectableClassNames;
-        this.producibleClassNames = producibleClassNames;
-        this.destructibleClassNames = destructibleClassNames;
-        this.pluginCategories = pluginCategories;
-    }
-
-    public List<String> getInjectableClassNames() {
-        return injectableClassNames;
-    }
-
-    public List<String> getProducibleClassNames() {
-        return producibleClassNames;
-    }
-
-    public List<String> getDestructibleClassNames() {
-        return destructibleClassNames;
-    }
-
-    public Map<String, List<String>> getPluginCategories() {
-        return pluginCategories;
-    }
-}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/spi/PluginModule.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/spi/PluginModule.java
new file mode 100644
index 0000000..279be74
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/spi/PluginModule.java
@@ -0,0 +1,54 @@
+/*
+ * 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.di.spi;
+
+import java.util.Map;
+import java.util.Set;
+
+public abstract class PluginModule {
+    private final Map<String, Set<String>> injectionBeans;
+    private final Map<String, Set<String>> producerBeans;
+    private final Set<String> destructorBeans;
+    private final Map<String, Set<String>> pluginBeans;
+
+    protected PluginModule(final Map<String, Set<String>> injectionBeans,
+                           final Map<String, Set<String>> producerBeans,
+                           final Set<String> destructorBeans,
+                           final Map<String, Set<String>> pluginBeans) {
+        this.injectionBeans = injectionBeans;
+        this.producerBeans = producerBeans;
+        this.destructorBeans = destructorBeans;
+        this.pluginBeans = pluginBeans;
+    }
+
+    public Map<String, Set<String>> getInjectionBeans() {
+        return injectionBeans;
+    }
+
+    public Map<String, Set<String>> getProducerBeans() {
+        return producerBeans;
+    }
+
+    public Set<String> getDestructorBeans() {
+        return destructorBeans;
+    }
+
+    public Map<String, Set<String>> getPluginBeans() {
+        return pluginBeans;
+    }
+}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/BeanProcessor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/BeanProcessor.java
index 013bab6..d2f1386 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/BeanProcessor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/BeanProcessor.java
@@ -21,7 +21,6 @@ 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.util.Strings;
 
 import javax.annotation.processing.AbstractProcessor;
 import javax.annotation.processing.RoundEnvironment;
@@ -35,30 +34,28 @@ import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.PackageElement;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
 import javax.lang.model.util.ElementKindVisitor9;
 import javax.lang.model.util.Elements;
 import javax.lang.model.util.SimpleAnnotationValueVisitor9;
 import javax.lang.model.util.Types;
-import javax.tools.FileObject;
-import javax.tools.JavaFileObject;
 import javax.tools.StandardLocation;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.UncheckedIOException;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeSet;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 // TODO: migrate to separate maven module between log4j-plugins and log4j-core
 @SupportedAnnotationTypes({"org.apache.logging.log4j.plugins.*", "org.apache.logging.log4j.core.config.plugins.*"})
 @SupportedOptions("pluginPackage")
 public class BeanProcessor extends AbstractProcessor {
-    public static final String BEAN_INFO_SERVICE_FILE = "META-INF/services/org.apache.logging.log4j.plugins.di.spi.BeanInfoService";
+    public static final String PLUGIN_MODULE_SERVICE_FILE = "META-INF/services/org.apache.logging.log4j.plugins.di.spi.PluginModule";
 
     public BeanProcessor() {
     }
@@ -214,45 +211,50 @@ public class BeanProcessor extends AbstractProcessor {
         final var pluginAnnotationVisitor = new PluginAnnotationVisitor();
         roundEnv.getElementsAnnotatedWithAny(pluginAnnotations).forEach(pluginAnnotationVisitor::visit);
 
-        final Set<ExecutableElement> producerMethods = producesAnnotationVisitor.producerMethods;
-        final Set<VariableElement> producerFields = producesAnnotationVisitor.producerFields;
-        final Set<ExecutableElement> disposesMethods = disposesAnnotationVisitor.disposesMethods;
-        final Set<TypeElement> injectableClasses = injectAnnotationVisitor.injectableClasses;
-        final Set<TypeElement> implicitInjectableClasses = qualifiedAnnotationVisitor.injectableClasses;
-        final Map<String, Set<TypeElement>> pluginCategories = pluginAnnotationVisitor.pluginCategories;
         final Set<PackageElement> packageElements = new HashSet<>();
 
         final Elements elements = processingEnv.getElementUtils();
-        final Set<CharSequence> producibleClassNames = Stream.concat(
-                producerMethods.stream()
-                        .map(e -> (TypeElement) e.getEnclosingElement())
-                        .peek(e -> packageElements.add(elements.getPackageOf(e)))
-                        .map(elements::getBinaryName),
-                producerFields.stream()
-                        .map(e -> (TypeElement) e.getEnclosingElement())
-                        .peek(e -> packageElements.add(elements.getPackageOf(e)))
-                        .map(elements::getBinaryName))
-                .collect(Collectors.toSet());
-        final Set<CharSequence> destructibleClassNames = disposesMethods.stream()
+        final Map<String, Set<String>> producerBeans = new HashMap<>();
+        for (final ExecutableElement producerMethod : producesAnnotationVisitor.producerMethods) {
+            final TypeElement declaringType = (TypeElement) producerMethod.getEnclosingElement();
+            packageElements.add(elements.getPackageOf(declaringType));
+            final String declaringClassName = elements.getBinaryName(declaringType).toString();
+            final Set<String> classNameHierarchy = getClassNameHierarchy(producerMethod.getReturnType());
+            producerBeans.put(declaringClassName, classNameHierarchy);
+        }
+        for (final VariableElement producerField : producesAnnotationVisitor.producerFields) {
+            final TypeElement declaringType = (TypeElement) producerField.getEnclosingElement();
+            packageElements.add(elements.getPackageOf(declaringType));
+            final String declaringClassName = elements.getBinaryName(declaringType).toString();
+            final Set<String> classNameHierarchy = getClassNameHierarchy(producerField.asType());
+            producerBeans.put(declaringClassName, classNameHierarchy);
+        }
+
+        final Set<CharSequence> destructibleClassNames = disposesAnnotationVisitor.disposesMethods.stream()
                 .map(e -> (TypeElement) e.getEnclosingElement())
                 .peek(e -> packageElements.add(elements.getPackageOf(e)))
                 .map(elements::getBinaryName)
                 .collect(Collectors.toSet());
-        final Set<CharSequence> injectableClassNames = Stream.concat(
-                injectableClasses.stream()
-                        .peek(e -> packageElements.add(elements.getPackageOf(e)))
-                        .map(elements::getBinaryName),
-                implicitInjectableClasses.stream()
-                        .peek(e -> packageElements.add(elements.getPackageOf(e)))
-                        .map(elements::getBinaryName))
-                .collect(Collectors.toSet());
-        final Map<String, List<CharSequence>> pluginClassNames = pluginCategories.entrySet().stream().collect(
-                Collectors.toMap(Map.Entry::getKey,
-                        entry -> entry.getValue().stream()
-                                .peek(el -> packageElements.add(elements.getPackageOf(el)))
-                                .map(elements::getBinaryName)
-                                .sorted(CharSequence::compare)
-                                .collect(Collectors.toList())));
+
+        final Map<String, Set<String>> injectionBeans = new HashMap<>();
+        final Set<TypeElement> injectionClasses = new HashSet<>(injectAnnotationVisitor.injectableClasses);
+        injectionClasses.addAll(qualifiedAnnotationVisitor.injectableClasses);
+        for (final TypeElement injectableClass : injectionClasses) {
+            packageElements.add(elements.getPackageOf(injectableClass));
+            final String declaringClassName = elements.getBinaryName(injectableClass).toString();
+            final Set<String> classNameHierarchy = getClassNameHierarchy(injectableClass.asType());
+            injectionBeans.put(declaringClassName, classNameHierarchy);
+        }
+
+        final Map<String, Set<String>> pluginBeans = pluginAnnotationVisitor.pluginCategories
+                .values()
+                .stream()
+                .flatMap(Set::stream)
+                .peek(typeElement -> packageElements.add(elements.getPackageOf(typeElement)))
+                .collect(Collectors.toMap(
+                        typeElement -> elements.getBinaryName(typeElement).toString(),
+                        typeElement -> getClassNameHierarchy(typeElement.asType())
+                ));
 
         String packageName = processingEnv.getOptions().get("pluginPackage");
         if (packageName == null) {
@@ -263,66 +265,97 @@ public class BeanProcessor extends AbstractProcessor {
                     .orElseThrow()
                     .toString();
         }
+        String className = processingEnv.getOptions().getOrDefault("pluginClassName", "Log4jModule");
         try {
-            writeBeanInfoServiceFile(packageName);
-            writeBeanInfoServiceClass(packageName, injectableClassNames, producibleClassNames, destructibleClassNames, pluginClassNames);
+            writePluginModule(packageName, className, injectionBeans, producerBeans, destructibleClassNames, pluginBeans);
             return false;
         } catch (IOException e) {
             throw new UncheckedIOException(e);
         }
     }
 
-    private void writeBeanInfoServiceFile(final String packageName) throws IOException {
-        final FileObject fileObject = processingEnv.getFiler()
-                .createResource(StandardLocation.CLASS_OUTPUT, "", BEAN_INFO_SERVICE_FILE);
-        try (PrintWriter out = new PrintWriter(fileObject.openWriter())) {
-            out.println(packageName + ".plugins.Log4jBeanInfo");
+    private void writePluginModule(final CharSequence packageName, final CharSequence className,
+                                   final Map<String, Set<String>> injectionBeans,
+                                   final Map<String, Set<String>> producerBeans,
+                                   final Set<CharSequence> destructorBeans,
+                                   final Map<String, Set<String>> pluginBeans) throws IOException {
+        try (final PrintWriter out = new PrintWriter(processingEnv.getFiler().createResource(
+                StandardLocation.CLASS_OUTPUT, "", PLUGIN_MODULE_SERVICE_FILE).openWriter())) {
+            out.println(packageName + ".plugins." + className);
         }
-    }
-
-    private void writeBeanInfoServiceClass(final String packageName, final Set<CharSequence> injectableClassNames,
-                                           final Set<CharSequence> producibleClassNames,
-                                           final Set<CharSequence> destructibleClassNames,
-                                           final Map<String, List<CharSequence>> pluginClassNames)
-            throws IOException {
-        final JavaFileObject sourceFile = processingEnv.getFiler()
-                .createSourceFile(packageName + ".plugins.Log4jBeanInfo");
-        try (final PrintWriter out = new PrintWriter(sourceFile.openWriter())) {
+        try (final PrintWriter out = new PrintWriter(processingEnv.getFiler().createSourceFile(
+                packageName + ".plugins." + className).openWriter())) {
             out.println("package " + packageName + ".plugins;");
             out.println();
-            out.println("import java.util.List;");
-            out.println("import java.util.Map;");
+            out.println("import java.util.*;");
+            out.println();
+            out.println("import org.apache.logging.log4j.plugins.di.spi.PluginModule;");
             out.println();
             out.println("@javax.annotation.processing.Generated(\"" + getClass().getName() + "\")");
-            out.println("public class Log4jBeanInfo extends org.apache.logging.log4j.plugins.di.spi.BeanInfoService {");
+            out.println("public class " + className + " extends PluginModule {");
             out.println();
-            out.println("  private static final List<String> INJECTABLE = List.of(" + getListOfNames(injectableClassNames) + ");");
+            out.println("  private static final Map<String, Set<String>> INJECT =");
+            out.println("    new TreeMap<>(Map.ofEntries(" + getEntries(injectionBeans) + "));");
             out.println();
-            out.println("  private static final List<String> PRODUCIBLE = List.of(" + getListOfNames(producibleClassNames) + ");");
+            out.println("  private static final Map<String, Set<String>> PRODUCE =");
+            out.println("    new TreeMap<>(Map.ofEntries(" + getEntries(producerBeans) + "));");
             out.println();
-            out.println("  private static final List<String> DESTRUCTIBLE = List.of(" + getListOfNames(destructibleClassNames) + ");");
+            out.println("  private static final Set<String> DESTROY = new TreeSet<>(Set.of(" + destructorBeans.stream().sorted(CharSequence::compare).collect(Collectors.joining("\",\n\"", "\n\"", "\"\n")) + "));");
             out.println();
-            out.println("  private static final Map<String, List<String>> PLUGIN_CATEGORIES = Map.of(" + getMapOfPluginNames(pluginClassNames) + ");");
+            out.println("  private static final Map<String, Set<String>> PLUGINS =");
+            out.println("    new TreeMap<>(Map.ofEntries(" + getEntries(pluginBeans) + "));");
             out.println();
-            out.println("  public Log4jBeanInfo() {");
-            out.println("    super(INJECTABLE, PRODUCIBLE, DESTRUCTIBLE, PLUGIN_CATEGORIES);");
+            out.println("  public " + className + "() {");
+            out.println("    super(INJECT, PRODUCE, DESTROY, PLUGINS);");
             out.println("  }");
             out.println();
             out.println("}");
         }
     }
 
-    private static String getListOfNames(final Set<CharSequence> names) {
-        return names.isEmpty() ? Strings.EMPTY : names.stream().sorted(CharSequence::compare).collect(
-                Collectors.joining("\",\n    \"", "\n    \"", "\"\n  "));
+    private static String getEntries(final Map<String, Set<String>> beans) {
+        return beans.entrySet().stream()
+                .sorted(Map.Entry.comparingByKey())
+                .map(e -> "Map.entry(\"" + e.getKey() + "\", new TreeSet<>(Set.of(" + e.getValue().stream().sorted().collect(Collectors.joining("\", \"", "\"", "\"")) + ")))")
+                .collect(Collectors.joining(",\n", "\n", "\n"));
+    }
+
+    private Set<String> getClassNameHierarchy(final TypeMirror base) {
+        final Set<String> hierarchy = new TreeSet<>();
+        walkClassHierarchy(base, hierarchy);
+        return hierarchy;
     }
 
-    private static String getMapOfPluginNames(final Map<String, List<CharSequence>> pluginClassNames) {
-        return pluginClassNames.isEmpty() ? Strings.EMPTY : pluginClassNames.entrySet()
-                .stream()
-                .sorted(Map.Entry.comparingByKey())
-                .map(e -> '"' + e.getKey() + "\", List.of(" + e.getValue().stream().collect(Collectors.joining("\",\n      \"", "\n      \"", "\"\n    ")) + ')')
-                .collect(Collectors.joining(",\n    ", "\n    ", "\n  "));
+    private void walkClassHierarchy(final TypeMirror base, final Set<String> encountered) {
+        final Types types = processingEnv.getTypeUtils();
+        final Elements elements = processingEnv.getElementUtils();
+        final Element element = types.asElement(base);
+        if (element != null) {
+            element.accept(new ElementKindVisitor9<Void, Void>() {
+                @Override
+                public Void visitTypeAsClass(final TypeElement e, final Void unused) {
+                    if (!elements.getPackageOf(e).getQualifiedName().contentEquals("java.lang")) {
+                        encountered.add(elements.getBinaryName(e).toString());
+                    }
+                    return null;
+                }
+
+                @Override
+                public Void visitTypeAsEnum(final TypeElement e, final Void unused) {
+                    encountered.add(elements.getBinaryName(e).toString());
+                    return null;
+                }
+
+                @Override
+                public Void visitTypeAsInterface(final TypeElement e, final Void unused) {
+                    encountered.add(elements.getBinaryName(e).toString());
+                    return null;
+                }
+            }, null);
+        }
+        for (final TypeMirror directSupertype : types.directSupertypes(base)) {
+            walkClassHierarchy(directSupertype, encountered);
+        }
     }
 
     private static CharSequence commonPrefix(final CharSequence str1, final CharSequence str2) {
diff --git a/log4j-plugins/src/main/java9/module-info.java b/log4j-plugins/src/main/java9/module-info.java
index 4506235..01e587d 100644
--- a/log4j-plugins/src/main/java9/module-info.java
+++ b/log4j-plugins/src/main/java9/module-info.java
@@ -32,10 +32,14 @@ module org.apache.logging.log4j.plugins {
     requires transitive org.apache.logging.log4j;
     requires transitive org.osgi.framework;
 
-    provides org.apache.logging.log4j.plugins.processor.PluginService with org.apache.logging.log4j.plugins.convert.plugins.Log4jPlugins;
-    provides org.apache.logging.log4j.plugins.di.spi.BeanInfoService with org.apache.logging.log4j.plugins.convert.plugins.Log4jBeanInfo;
-    provides javax.annotation.processing.Processor with org.apache.logging.log4j.plugins.processor.PluginProcessor, org.apache.logging.log4j.plugins.processor.BeanProcessor;
+    provides org.apache.logging.log4j.plugins.processor.PluginService with
+            org.apache.logging.log4j.plugins.convert.plugins.Log4jPlugins;
+    provides org.apache.logging.log4j.plugins.di.spi.PluginModule with
+            org.apache.logging.log4j.plugins.convert.plugins.Log4jModule;
+    provides javax.annotation.processing.Processor with
+            org.apache.logging.log4j.plugins.processor.PluginProcessor,
+            org.apache.logging.log4j.plugins.processor.BeanProcessor;
 
     uses org.apache.logging.log4j.plugins.processor.PluginService;
-//    uses org.apache.logging.log4j.plugins.di.spi.BeanInfoService;
+//    uses org.apache.logging.log4j.plugins.di.spi.PluginModule;
 }
diff --git a/log4j-plugins/src/test/java-test9/module-info.java b/log4j-plugins/src/test/java-test9/module-info.java
index 529310c..3ac8a6a 100644
--- a/log4j-plugins/src/test/java-test9/module-info.java
+++ b/log4j-plugins/src/test/java-test9/module-info.java
@@ -4,5 +4,8 @@ module org.apache.logging.log4j.plugins.test {
     requires org.apache.logging.log4j;
     requires org.apache.logging.log4j.plugins;
 
-    provides org.apache.logging.log4j.plugins.processor.PluginService with org.apache.logging.log4j.plugins.test.validation.plugins.Log4jPlugins;
+    provides org.apache.logging.log4j.plugins.processor.PluginService with
+            org.apache.logging.log4j.plugins.test.validation.plugins.Log4jPlugins;
+    provides org.apache.logging.log4j.plugins.di.spi.PluginModule with
+            org.apache.logging.log4j.plugins.test.validation.plugins.Log4jModule;
 }
diff --git a/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/BeanProcessorTest.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/BeanProcessorTest.java
index b63070c..7afe944 100644
--- a/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/BeanProcessorTest.java
+++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/BeanProcessorTest.java
@@ -17,38 +17,25 @@
 
 package org.apache.logging.log4j.plugins.processor;
 
-import org.apache.logging.log4j.plugins.di.spi.BeanInfoService;
+import org.apache.logging.log4j.plugins.di.spi.PluginModule;
 import org.junit.jupiter.api.Test;
 
-import java.util.List;
 import java.util.ServiceLoader;
 
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class BeanProcessorTest {
     @Test
-    void canLoadTestCategory() {
-        final BeanInfoService service = ServiceLoader.load(BeanInfoService.class, getClass().getClassLoader())
-                .findFirst()
-                .orElseThrow();
-        final List<String> testPlugins = service.getPluginCategories().get("Test");
-        assertNotNull(testPlugins);
-        assertNotEquals(0, testPlugins.size());
-        assertTrue(testPlugins.stream().anyMatch(name -> name.equals(FakePlugin.class.getName())));
-    }
-
-    @Test
     void smokeTests() {
-        final BeanInfoService service = ServiceLoader.load(BeanInfoService.class, getClass().getClassLoader())
+        final PluginModule service = ServiceLoader.load(PluginModule.class, getClass().getClassLoader())
                 .findFirst()
                 .orElseThrow();
-        assertTrue(service.getInjectableClassNames().stream().anyMatch(name -> name.equals("org.apache.logging.log4j.plugins.test.validation.ExampleBean")));
-        assertTrue(service.getInjectableClassNames().stream().anyMatch(name -> name.equals("org.apache.logging.log4j.plugins.test.validation.ImplicitBean")));
-        assertTrue(service.getInjectableClassNames().stream().anyMatch(name -> name.equals("org.apache.logging.log4j.plugins.test.validation.ImplicitMethodBean")));
-        assertTrue(service.getInjectableClassNames().stream().anyMatch(name -> name.equals("org.apache.logging.log4j.plugins.test.validation.ProductionBean$Builder")));
-        assertTrue(service.getProducibleClassNames().stream().anyMatch(name -> name.equals("org.apache.logging.log4j.plugins.test.validation.ProductionBean")));
-        assertTrue(service.getDestructibleClassNames().stream().anyMatch(name -> name.equals("org.apache.logging.log4j.plugins.test.validation.ProductionBean")));
+        assertTrue(service.getInjectionBeans().containsKey("org.apache.logging.log4j.plugins.test.validation.ExampleBean"));
+        assertTrue(service.getInjectionBeans().containsKey("org.apache.logging.log4j.plugins.test.validation.ImplicitBean"));
+        assertTrue(service.getInjectionBeans().containsKey("org.apache.logging.log4j.plugins.test.validation.ImplicitMethodBean"));
+        assertTrue(service.getInjectionBeans().containsKey("org.apache.logging.log4j.plugins.test.validation.ProductionBean$Builder"));
+        assertTrue(service.getProducerBeans().containsKey("org.apache.logging.log4j.plugins.test.validation.ProductionBean"));
+        assertTrue(service.getDestructorBeans().contains("org.apache.logging.log4j.plugins.test.validation.ProductionBean"));
+        assertTrue(service.getPluginBeans().containsKey(FakePlugin.class.getName()));
     }
 }
\ No newline at end of file
diff --git a/log4j-plugins/src/test/java9/module-info.java b/log4j-plugins/src/test/java9/module-info.java
index 41a87f2..4195946 100644
--- a/log4j-plugins/src/test/java9/module-info.java
+++ b/log4j-plugins/src/test/java9/module-info.java
@@ -23,10 +23,14 @@ open module org.apache.logging.log4j.plugins {
     requires org.junit.platform.engine;
     requires junit;
 
-    provides org.apache.logging.log4j.plugins.processor.PluginService with org.apache.logging.log4j.plugins.convert.plugins.Log4jPlugins;
-    provides org.apache.logging.log4j.plugins.di.spi.BeanInfoService with org.apache.logging.log4j.plugins.convert.plugins.Log4jBeanInfo;
-    provides javax.annotation.processing.Processor with org.apache.logging.log4j.plugins.processor.PluginProcessor, org.apache.logging.log4j.plugins.processor.BeanProcessor;
+    provides org.apache.logging.log4j.plugins.processor.PluginService with
+            org.apache.logging.log4j.plugins.convert.plugins.Log4jPlugins;
+    provides org.apache.logging.log4j.plugins.di.spi.PluginModule with
+            org.apache.logging.log4j.plugins.convert.plugins.Log4jModule;
+    provides javax.annotation.processing.Processor with
+            org.apache.logging.log4j.plugins.processor.PluginProcessor,
+            org.apache.logging.log4j.plugins.processor.BeanProcessor;
 
     uses org.apache.logging.log4j.plugins.processor.PluginService;
-    uses org.apache.logging.log4j.plugins.di.spi.BeanInfoService;
+    uses org.apache.logging.log4j.plugins.di.spi.PluginModule;
 }
diff --git a/pom.xml b/pom.xml
index 3177d6b..5217a8f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
+
 <!--
   ~ Licensed to the Apache Software Foundation (ASF) under one or more
   ~ contributor license agreements. See the NOTICE file distributed with
@@ -1099,7 +1100,6 @@
                 <version>${errorprone.version}</version>
               </path>
             </annotationProcessorPaths>
-            <forceJavacCompilerUse>true</forceJavacCompilerUse>
             <parameters>true</parameters>
           </configuration>
         </plugin>