You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2022/01/02 01:08:12 UTC

[logging-log4j2] branch master updated (69398f2 -> a7ca777)

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

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


    from 69398f2  Port from release-2.x, can't cherry-pick thanks to jpms.
     add 2344d35  Add v3 annotation API
     add bc2e767  Add v3 dependency injection SPI
     add 379234d  Add v3 dependency injection default implementation and tests
     add 1ed2584  Add missing class
     add 6dc0812  Use weak cache for type closure lookup
     add cb9c012  Refactor cache API
     add a827c77  Refactor Collection<Qualifier> into own class
     add d537be3  Simplify disposer method type matching
     add bb256a7  Extract internal code into SPI interface
     add 7906e63  Combine scoped and bean packages and other docs
     add b4d7cfc  Merge Scoped into Bean
     add 5e17845  Clean up initialization context usage in bean runner
     add 3aeec75  Remove BeanInstance class
     add 03be319  Remove stereotypes
     add 37e122b  Simplify InjectionFactory into Injector
     add 4bcdc58  Update exceptions
     add b72de60  Simplify producer code and optimizations
     add 5f86788  Merge branch 'master' into mean-bean-machine
     add 0184771  Remove unused method and rename
     add eba9421  Add support for default @Named value
     add e255f43  Improve toString representation
     add 915a659  Use decapitalized simple class name for default @Named value
     add 3a6fee3  Use better exception type
     add f089940  Remove unused type parameter in Variable
     add a11f179  Use better exceptions
     add e9c1233  Decrease visibility
     add 41f31dd  Improve toString
     add 37aacb1  Remove unused import
     add 0fe2003  Improve annotation meta-modeling
     add b95c818  Remove unused type parameters
     add 41b8043  Add variable withers
     add b228c68  Removed unused methods
     add aaccfb9  Use normalized internal state for qualifers
     add a3d7805  Rename scope annotations to use Scoped suffix
     add 9043b20  Add tests around deferred instantiation
     add 546ab29  Make test more explicit
     add 5138f52  Split bean loading and validation
     add a7fbac6  Clean up use of Variable
     add f135d48  Make behavior match updated docs
     add 60cc78c  Avoid LoD violations
     add 20f23a7  Remove redundant instanceof
     add 5031dd6  Remove unneeded methods
     add 0770786  Specify targets for scopes
     add 7f8995b  Rearrange code for clarity
     add 4e7b905  Extract ProviderFactory interface
     add 5c2aa2f  Merge branch 'master' into mean-bean-machine
     add 5ae774a  Inline Qualifiers
     add 65e0d87  Remove unused class
     add 45306c2  Merge remote-tracking branch 'origin/master' into mean-bean-machine
     add b381d8d  Merge branch 'master' into mean-bean-machine
     add 221b364  Merge branch 'master' into mean-bean-machine
     add 78e1f10  Replace java.beans use with BeanUtils
     add 788da26  Merge branch 'master' into mean-bean-machine
     add 317969f  Merge branch 'master' into mean-bean-machine
     add c83c9a1  Move DI SPI to log4j-core
     add cd461f1  Add more DI API docs with some renames
     add 79561f8  Add more DI API docs
     add f7ea16e  Simplify reflection model and qualifiers matching
     add 9eb91f9  Merge branch 'master' into mean-bean-machine
     add 6caab9e  Clean up BeanManager API surface
     add a789af3  Add tests and stable ordering for bean inheritance
     add c7dc660  Merge branch 'master' into mean-bean-machine
     add c4b8807  Merge branch 'master' into mean-bean-machine
     add 9efae8a  Merge branch 'master' into mean-bean-machine
     add 64f8151  Fix compile warning
     add 8000a00  Improve docs
     add 7ff8c9c  Add foundation for bean annotation processing and plugin metadata
     add b4545a4  Add LoggerContextScoped annotation
     add c3c88f5  Add type hierarchy data to plugin modules
     add 2d3e8f0  Use proper producer annotation for bridging APIs
     add 1e33a72  Check for scope of bean class as fallback
     add 4bf8c3e  Add richer plugin bean metadata generation
     new a7ca777  Merge branch 'mean-bean-machine'

The 1 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:
 .../java/org/apache/logging/log4j/LoggerTest.java  |   2 +-
 .../logging/log4j/NoopThreadContextTest.java       |   2 +
 .../log4j/core/config/di/BeanManagerTest.java      | 605 +++++++++++++++++++++
 .../log4j/core/config/di/InjectionPointTest.java   | 136 +++++
 .../log4j/core/config/di/InjectionTargetTest.java  | 109 ++++
 log4j-core-test/src/test/java9/module-info.java    |   2 +
 log4j-core/src/main/java/module-info.java          |   2 +
 .../core/config/di/AmbiguousBeanException.java     |  12 +-
 .../apache/logging/log4j/core/config/di/Bean.java  |  98 ++++
 .../logging/log4j/core/config/di/BeanManager.java  | 265 +++++++++
 .../log4j/core/config/di/DefinitionException.java  |  10 +-
 .../core/config/di/IllegalProductException.java    |  10 +-
 .../core/config/di/InitializationContext.java      |  54 ++
 .../core/config/di/InitializationException.java    |  10 +-
 .../log4j/core/config/di/InjectionException.java   |  14 +-
 .../log4j/core/config/di/InjectionPoint.java       |  55 ++
 .../log4j/core/config/di/InjectionTarget.java      |  63 +++
 .../core/config/di/InjectionTargetFactory.java     |   8 +-
 .../LoggerContextScoped.java}                      |  21 +-
 .../logging/log4j/core/config/di/Producer.java     |  67 +++
 .../log4j/core/config/di/ProducerFactory.java      |   9 +-
 .../log4j/core/config/di/ResolutionException.java  |  10 +-
 .../logging/log4j/core/config/di/ScopeContext.java |  63 +++
 .../core/config/di/UnsatisfiedBeanException.java   |  18 +-
 .../log4j/core/config/di/ValidationException.java  |  25 +-
 .../log4j/core/config/di/impl/AbstractBean.java    |  76 +++
 .../core/config/di/impl/AbstractProducer.java      |  74 +++
 .../config/di/impl/AbstractProducerFactory.java    |  42 ++
 .../core/config/di/impl/DefaultBeanManager.java    | 460 ++++++++++++++++
 .../di/impl/DefaultInitializationContext.java      | 134 +++++
 .../core/config/di/impl/DefaultInjectionPoint.java | 121 +++++
 .../config/di/impl/DefaultInjectionTarget.java     | 136 +++++
 .../di/impl/DefaultInjectionTargetFactory.java     | 160 ++++++
 .../core/config/di/impl/DefaultScopeContext.java   |  72 +++
 .../core/config/di/impl/DependentScopeContext.java |  58 ++
 .../log4j/core/config/di/impl/FieldProducer.java   |  67 +++
 .../core/config/di/impl/FieldProducerFactory.java  |  44 ++
 .../core/config/di/impl/InjectionTargetBean.java   |  86 +++
 .../log4j/core/config/di/impl/Injector.java        | 111 ++++
 .../log4j/core/config/di/impl/MethodProducer.java  |  63 +++
 .../core/config/di/impl/MethodProducerFactory.java |  47 ++
 .../log4j/core/config/di/impl/OptionalBean.java    |  83 +++
 .../log4j/core/config/di/impl/ProducerBean.java    |  94 ++++
 .../log4j/core/config/di/impl/ProvidedBean.java    |  77 +++
 .../log4j/core/config/di/impl/ProviderBean.java    |  77 +++
 .../log4j/core/config/plugins/PluginAliases.java   |  10 +-
 .../log4j/core/config/plugins/PluginAttribute.java |   5 +
 .../config/plugins/PluginBuilderAttribute.java     |   5 +
 .../core/config/plugins/PluginBuilderFactory.java  |   9 +-
 .../core/config/plugins/PluginConfiguration.java   |   2 +
 .../log4j/core/config/plugins/PluginElement.java   |   5 +
 .../log4j/core/config/plugins/PluginFactory.java   |   9 +-
 .../log4j/core/config/plugins/PluginNode.java      |   2 +
 .../log4j/core/config/plugins/PluginValue.java     |   5 +
 .../config/plugins/util/PluginAliasesProvider.java |  20 +-
 .../plugins/util/PluginAttributeNameProvider.java  |  20 +-
 .../util/PluginBuilderAttributeNameProvider.java   |  20 +-
 .../plugins/util/PluginElementNameProvider.java    |  20 +-
 .../plugins/util/PluginValueNameProvider.java      |  20 +-
 .../log4j/plugin/processor/BeanProcessor.java      | 516 ++++++++++++++++++
 .../log4j/plugin/processor/PluginProcessor.java    |  15 +-
 .../src/main/java9/module-info.java                |   4 +-
 .../services/javax.annotation.processing.Processor |   1 +
 log4j-plugins-test/pom.xml                         |   8 +
 .../plugins/test/validation/ExampleBean.java}      |  34 +-
 .../log4j/plugins/test/validation}/FakePlugin.java |   2 +-
 .../plugins/test/validation/ImplicitBean.java}     |  33 +-
 .../test/validation/ImplicitMethodBean.java}       |  42 +-
 .../plugins/test/validation/ProductionBean.java    |  56 ++
 log4j-plugins-test/src/main/java9/module-info.java |   1 +
 .../log4j/plugin/processor/BeanProcessorTest.java  |  51 ++
 .../plugin/processor/PluginProcessorTest.java      |   9 +-
 .../plugins/convert/TypeConverterRegistryTest.java |  10 +-
 log4j-plugins/src/main/java/module-info.java       |   4 +-
 .../org/apache/logging/log4j/plugins/Plugin.java   |   3 +
 .../logging/log4j/plugins/PluginAliases.java       |   6 +
 .../logging/log4j/plugins/PluginAttribute.java     |   2 +
 .../log4j/plugins/PluginBuilderAttribute.java      |   2 +
 .../logging/log4j/plugins/PluginElement.java       |   2 +
 .../logging/log4j/plugins/PluginFactory.java       |   3 +
 .../apache/logging/log4j/plugins/PluginNode.java   |   2 +
 .../apache/logging/log4j/plugins/PluginValue.java  |   2 +
 .../logging/log4j/plugins/di/DependentScoped.java  |  27 +-
 .../plugins/{PluginNode.java => di/Disposes.java}  |  20 +-
 .../apache/logging/log4j/plugins/di/Inject.java    |  51 ++
 .../org/apache/logging/log4j/plugins/di/Named.java |  26 +-
 .../{PluginAliases.java => di/NamedAliases.java}   |  25 +-
 .../logging/log4j/plugins/di/PostConstruct.java    |  18 +-
 .../logging/log4j/plugins/di/PreDestroy.java       |  16 +-
 .../apache/logging/log4j/plugins/di/Producer.java  |  19 +-
 .../apache/logging/log4j/plugins/di/Produces.java  |  53 ++
 .../apache/logging/log4j/plugins/di/Provider.java  |   8 +-
 .../apache/logging/log4j/plugins/di/Qualifier.java |  19 +-
 .../apache/logging/log4j/plugins/di/ScopeType.java |  21 +-
 .../logging/log4j/plugins/di/SingletonScoped.java  |  19 +-
 .../log4j/plugins/di/model/DisposesMethod.java     |  53 ++
 .../log4j/plugins/di/model/GenericPlugin.java      |  35 +-
 .../log4j/plugins/di/model/InjectionTarget.java    |  49 ++
 .../log4j/plugins/di/model/PluginModule.java       |  18 +-
 .../log4j/plugins/di/model/PluginSource.java       |  15 +-
 .../log4j/plugins/di/model/ProducerField.java      |  55 ++
 .../log4j/plugins/di/model/ProducerMethod.java     |  62 +++
 .../logging/log4j/plugins/di/package-info.java     |  15 +-
 .../inject/AbstractConfigurationInjector.java      |   8 +-
 .../log4j/plugins/name/AliasesProvider.java        |  16 +-
 .../name/AnnotatedElementAliasesProvider.java      |  46 ++
 .../plugins/name/AnnotatedElementNameProvider.java |  56 +-
 .../log4j/plugins/name/NamedAliasesProvider.java   |  54 ++
 .../plugins/name/NamedQualifierNameProvider.java   |  19 +-
 .../log4j/plugins/name/PluginAliasesProvider.java  |  17 +-
 .../log4j/plugins/name/PluginNameProvider.java     |  16 +-
 .../log4j/plugins/processor/PluginService.java     |   4 +-
 .../logging/log4j/plugins/util/AnnotationUtil.java |  25 +-
 .../apache/logging/log4j/plugins/util/Cache.java   |  11 +-
 .../logging/log4j/plugins/util/LazyValue.java      |  64 +++
 .../log4j/plugins/util/ParameterizedTypeImpl.java  |  67 +++
 .../logging/log4j/plugins/util/TypeUtil.java       | 355 +++++++++++-
 .../apache/logging/log4j/plugins/util/Value.java   |  11 +-
 .../logging/log4j/plugins/util/WeakCache.java      |  60 ++
 .../logging/log4j/plugins/util/WeakLazyValue.java  |  58 ++
 120 files changed, 5798 insertions(+), 425 deletions(-)
 create mode 100644 log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/di/BeanManagerTest.java
 create mode 100644 log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/di/InjectionPointTest.java
 create mode 100644 log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/di/InjectionTargetTest.java
 copy log4j-plugins-test/src/main/java9/module-info.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/AmbiguousBeanException.java (70%)
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/Bean.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/BeanManager.java
 copy log4j-plugins-test/src/main/java9/module-info.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/DefinitionException.java (70%)
 copy log4j-plugins-test/src/main/java9/module-info.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/IllegalProductException.java (70%)
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InitializationContext.java
 copy log4j-plugins-test/src/main/java9/module-info.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InitializationException.java (70%)
 copy log4j-plugins-test/src/main/java9/module-info.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InjectionException.java (70%)
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InjectionPoint.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InjectionTarget.java
 copy log4j-plugins-test/src/main/java9/module-info.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InjectionTargetFactory.java (70%)
 copy log4j-core/src/main/java/org/apache/logging/log4j/core/config/{plugins/PluginNode.java => di/LoggerContextScoped.java} (68%)
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/Producer.java
 copy log4j-plugins-test/src/main/java9/module-info.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ProducerFactory.java (70%)
 copy log4j-plugins-test/src/main/java9/module-info.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ResolutionException.java (70%)
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ScopeContext.java
 copy log4j-plugin-processor/src/main/java9/module-info.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/UnsatisfiedBeanException.java (67%)
 copy log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ValidationException.java (50%)
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/AbstractBean.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/AbstractProducer.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/AbstractProducerFactory.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInitializationContext.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionPoint.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTarget.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTargetFactory.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultScopeContext.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DependentScopeContext.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/FieldProducer.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/FieldProducerFactory.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/InjectionTargetBean.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/Injector.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/MethodProducer.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/MethodProducerFactory.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/OptionalBean.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/ProducerBean.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/ProvidedBean.java
 create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/ProviderBean.java
 copy log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginAliasesProvider.java (62%)
 copy log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginAttributeNameProvider.java (60%)
 copy log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginBuilderAttributeNameProvider.java (59%)
 copy log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginElementNameProvider.java (60%)
 copy log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java => log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginValueNameProvider.java (61%)
 create mode 100644 log4j-plugin-processor/src/main/java/org/apache/logging/log4j/plugin/processor/BeanProcessor.java
 copy log4j-plugins-test/src/{test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java => main/java/org/apache/logging/log4j/plugins/test/validation/ExampleBean.java} (56%)
 copy log4j-plugins-test/src/{test/java/org/apache/logging/log4j/plugins/processor => main/java/org/apache/logging/log4j/plugins/test/validation}/FakePlugin.java (95%)
 copy log4j-plugins-test/src/{test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java => main/java/org/apache/logging/log4j/plugins/test/validation/ImplicitBean.java} (56%)
 copy log4j-plugins-test/src/{test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java => main/java/org/apache/logging/log4j/plugins/test/validation/ImplicitMethodBean.java} (50%)
 create mode 100644 log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/ProductionBean.java
 create mode 100644 log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugin/processor/BeanProcessorTest.java
 copy log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginElement.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/DependentScoped.java (55%)
 copy log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/{PluginNode.java => di/Disposes.java} (60%)
 create mode 100644 log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Inject.java
 copy log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginNode.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java (63%)
 copy log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/{PluginAliases.java => di/NamedAliases.java} (71%)
 copy log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginNode.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/PostConstruct.java (68%)
 copy log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginNode.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/PreDestroy.java (68%)
 copy log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Producer.java (71%)
 create mode 100644 log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Produces.java
 copy log4j-plugins-test/src/main/java9/module-info.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Provider.java (70%)
 copy log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Qualifier.java (71%)
 copy log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginNode.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/ScopeType.java (65%)
 copy log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginNode.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/SingletonScoped.java (68%)
 create mode 100644 log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/DisposesMethod.java
 copy log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/GenericPlugin.java (51%)
 create mode 100644 log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/InjectionTarget.java
 copy log4j-plugins-test/src/main/java9/module-info.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginModule.java (68%)
 copy log4j-plugins-test/src/main/java9/module-info.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginSource.java (70%)
 create mode 100644 log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerField.java
 create mode 100644 log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerMethod.java
 copy log4j-plugins-test/src/main/java9/module-info.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/package-info.java (69%)
 copy log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginNode.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/AliasesProvider.java (68%)
 create mode 100644 log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/AnnotatedElementAliasesProvider.java
 create mode 100644 log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/NamedAliasesProvider.java
 copy log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/NamedQualifierNameProvider.java (66%)
 copy log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/PluginAliasesProvider.java (71%)
 copy log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/PluginNameProvider.java (71%)
 rename log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/processor/FakePlugin.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/AnnotationUtil.java (58%)
 copy log4j-plugins-test/src/main/java9/module-info.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/Cache.java (70%)
 create mode 100644 log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/LazyValue.java
 create mode 100644 log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/ParameterizedTypeImpl.java
 copy log4j-plugins-test/src/main/java9/module-info.java => log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/Value.java (70%)
 create mode 100644 log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/WeakCache.java
 create mode 100644 log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/WeakLazyValue.java

[logging-log4j2] 01/01: Merge branch 'mean-bean-machine'

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

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

commit a7ca777c74a99f97703676fbfc96773b6400c3c8
Merge: 69398f2 4bf8c3e
Author: Matt Sicker <ma...@apache.org>
AuthorDate: Sat Jan 1 19:07:44 2022 -0600

    Merge branch 'mean-bean-machine'

 .../java/org/apache/logging/log4j/LoggerTest.java  |   2 +-
 .../logging/log4j/NoopThreadContextTest.java       |   2 +
 .../log4j/core/config/di/BeanManagerTest.java      | 605 +++++++++++++++++++++
 .../log4j/core/config/di/InjectionPointTest.java   | 136 +++++
 .../log4j/core/config/di/InjectionTargetTest.java  | 109 ++++
 log4j-core-test/src/test/java9/module-info.java    |   2 +
 log4j-core/src/main/java/module-info.java          |   2 +
 .../core/config/di/AmbiguousBeanException.java     |  12 +-
 .../apache/logging/log4j/core/config/di/Bean.java  |  98 ++++
 .../logging/log4j/core/config/di/BeanManager.java  | 265 +++++++++
 .../log4j/core/config/di/DefinitionException.java  |  10 +-
 .../core/config/di/IllegalProductException.java    |  10 +-
 .../core/config/di/InitializationContext.java      |  54 ++
 .../core/config/di/InitializationException.java    |  10 +-
 .../log4j/core/config/di/InjectionException.java   |  14 +-
 .../log4j/core/config/di/InjectionPoint.java       |  55 ++
 .../log4j/core/config/di/InjectionTarget.java      |  63 +++
 .../core/config/di/InjectionTargetFactory.java     |   8 +-
 .../LoggerContextScoped.java}                      |  21 +-
 .../logging/log4j/core/config/di/Producer.java     |  67 +++
 .../log4j/core/config/di/ProducerFactory.java      |   9 +-
 .../log4j/core/config/di/ResolutionException.java  |  10 +-
 .../logging/log4j/core/config/di/ScopeContext.java |  63 +++
 .../core/config/di/UnsatisfiedBeanException.java   |  18 +-
 .../log4j/core/config/di/ValidationException.java  |  25 +-
 .../log4j/core/config/di/impl/AbstractBean.java    |  76 +++
 .../core/config/di/impl/AbstractProducer.java      |  74 +++
 .../config/di/impl/AbstractProducerFactory.java    |  42 ++
 .../core/config/di/impl/DefaultBeanManager.java    | 460 ++++++++++++++++
 .../di/impl/DefaultInitializationContext.java      | 134 +++++
 .../core/config/di/impl/DefaultInjectionPoint.java | 121 +++++
 .../config/di/impl/DefaultInjectionTarget.java     | 136 +++++
 .../di/impl/DefaultInjectionTargetFactory.java     | 160 ++++++
 .../core/config/di/impl/DefaultScopeContext.java   |  72 +++
 .../core/config/di/impl/DependentScopeContext.java |  58 ++
 .../log4j/core/config/di/impl/FieldProducer.java   |  67 +++
 .../core/config/di/impl/FieldProducerFactory.java  |  44 ++
 .../core/config/di/impl/InjectionTargetBean.java   |  86 +++
 .../log4j/core/config/di/impl/Injector.java        | 111 ++++
 .../log4j/core/config/di/impl/MethodProducer.java  |  63 +++
 .../core/config/di/impl/MethodProducerFactory.java |  47 ++
 .../log4j/core/config/di/impl/OptionalBean.java    |  83 +++
 .../log4j/core/config/di/impl/ProducerBean.java    |  94 ++++
 .../log4j/core/config/di/impl/ProvidedBean.java    |  77 +++
 .../log4j/core/config/di/impl/ProviderBean.java    |  77 +++
 .../log4j/core/config/plugins/PluginAliases.java   |  10 +-
 .../log4j/core/config/plugins/PluginAttribute.java |   5 +
 .../config/plugins/PluginBuilderAttribute.java     |   5 +
 .../core/config/plugins/PluginBuilderFactory.java  |   9 +-
 .../core/config/plugins/PluginConfiguration.java   |   2 +
 .../log4j/core/config/plugins/PluginElement.java   |   5 +
 .../log4j/core/config/plugins/PluginFactory.java   |   9 +-
 .../log4j/core/config/plugins/PluginNode.java      |   2 +
 .../log4j/core/config/plugins/PluginValue.java     |   5 +
 .../config/plugins/util/PluginAliasesProvider.java |  20 +-
 .../plugins/util/PluginAttributeNameProvider.java  |  20 +-
 .../util/PluginBuilderAttributeNameProvider.java   |  20 +-
 .../plugins/util/PluginElementNameProvider.java    |  20 +-
 .../plugins/util/PluginValueNameProvider.java      |  20 +-
 .../log4j/plugin/processor/BeanProcessor.java      | 516 ++++++++++++++++++
 .../log4j/plugin/processor/PluginProcessor.java    |  15 +-
 .../src/main/java9/module-info.java                |   4 +-
 .../services/javax.annotation.processing.Processor |   1 +
 log4j-plugins-test/pom.xml                         |   8 +
 .../plugins/test/validation/ExampleBean.java}      |  34 +-
 .../log4j/plugins/test/validation}/FakePlugin.java |   2 +-
 .../plugins/test/validation/ImplicitBean.java}     |  33 +-
 .../test/validation/ImplicitMethodBean.java}       |  42 +-
 .../plugins/test/validation/ProductionBean.java    |  56 ++
 log4j-plugins-test/src/main/java9/module-info.java |   1 +
 .../log4j/plugin/processor/BeanProcessorTest.java  |  51 ++
 .../plugin/processor/PluginProcessorTest.java      |   9 +-
 .../plugins/convert/TypeConverterRegistryTest.java |  10 +-
 log4j-plugins/src/main/java/module-info.java       |   4 +-
 .../org/apache/logging/log4j/plugins/Plugin.java   |   3 +
 .../logging/log4j/plugins/PluginAliases.java       |   6 +
 .../logging/log4j/plugins/PluginAttribute.java     |   2 +
 .../log4j/plugins/PluginBuilderAttribute.java      |   2 +
 .../logging/log4j/plugins/PluginElement.java       |   2 +
 .../logging/log4j/plugins/PluginFactory.java       |   3 +
 .../apache/logging/log4j/plugins/PluginNode.java   |   2 +
 .../apache/logging/log4j/plugins/PluginValue.java  |   2 +
 .../logging/log4j/plugins/di/DependentScoped.java  |  27 +-
 .../plugins/{PluginNode.java => di/Disposes.java}  |  20 +-
 .../apache/logging/log4j/plugins/di/Inject.java    |  51 ++
 .../org/apache/logging/log4j/plugins/di/Named.java |  26 +-
 .../{PluginAliases.java => di/NamedAliases.java}   |  25 +-
 .../logging/log4j/plugins/di/PostConstruct.java    |  18 +-
 .../logging/log4j/plugins/di/PreDestroy.java       |  16 +-
 .../apache/logging/log4j/plugins/di/Producer.java  |  19 +-
 .../apache/logging/log4j/plugins/di/Produces.java  |  53 ++
 .../apache/logging/log4j/plugins/di/Provider.java  |   8 +-
 .../apache/logging/log4j/plugins/di/Qualifier.java |  19 +-
 .../apache/logging/log4j/plugins/di/ScopeType.java |  21 +-
 .../logging/log4j/plugins/di/SingletonScoped.java  |  19 +-
 .../log4j/plugins/di/model/DisposesMethod.java     |  53 ++
 .../log4j/plugins/di/model/GenericPlugin.java      |  35 +-
 .../log4j/plugins/di/model/InjectionTarget.java    |  49 ++
 .../log4j/plugins/di/model/PluginModule.java       |  18 +-
 .../log4j/plugins/di/model/PluginSource.java       |  15 +-
 .../log4j/plugins/di/model/ProducerField.java      |  55 ++
 .../log4j/plugins/di/model/ProducerMethod.java     |  62 +++
 .../logging/log4j/plugins/di/package-info.java     |  15 +-
 .../inject/AbstractConfigurationInjector.java      |   8 +-
 .../log4j/plugins/name/AliasesProvider.java        |  16 +-
 .../name/AnnotatedElementAliasesProvider.java      |  46 ++
 .../plugins/name/AnnotatedElementNameProvider.java |  56 +-
 .../log4j/plugins/name/NamedAliasesProvider.java   |  54 ++
 .../plugins/name/NamedQualifierNameProvider.java   |  19 +-
 .../log4j/plugins/name/PluginAliasesProvider.java  |  17 +-
 .../log4j/plugins/name/PluginNameProvider.java     |  16 +-
 .../log4j/plugins/processor/PluginService.java     |   4 +-
 .../logging/log4j/plugins/util/AnnotationUtil.java |  25 +-
 .../apache/logging/log4j/plugins/util/Cache.java   |  11 +-
 .../logging/log4j/plugins/util/LazyValue.java      |  64 +++
 .../log4j/plugins/util/ParameterizedTypeImpl.java  |  67 +++
 .../logging/log4j/plugins/util/TypeUtil.java       | 355 +++++++++++-
 .../apache/logging/log4j/plugins/util/Value.java   |  11 +-
 .../logging/log4j/plugins/util/WeakCache.java      |  60 ++
 .../logging/log4j/plugins/util/WeakLazyValue.java  |  58 ++
 120 files changed, 5798 insertions(+), 425 deletions(-)

diff --cc log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java
index d98f0ea,0000000..27a3a8e
mode 100644,000000..100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java
@@@ -1,634 -1,0 +1,634 @@@
 +/*
 + * 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;
 +
 +import org.apache.logging.log4j.message.EntryMessage;
 +import org.apache.logging.log4j.message.JsonMessage;
 +import org.apache.logging.log4j.message.Message;
 +import org.apache.logging.log4j.message.MessageFactory;
 +import org.apache.logging.log4j.message.ObjectMessage;
 +import org.apache.logging.log4j.message.ParameterizedMessageFactory;
 +import org.apache.logging.log4j.message.SimpleMessage;
 +import org.apache.logging.log4j.message.SimpleMessageFactory;
 +import org.apache.logging.log4j.message.StringFormatterMessageFactory;
 +import org.apache.logging.log4j.message.StructuredDataMessage;
 +import org.apache.logging.log4j.test.TestLogger;
 +import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
 +import org.apache.logging.log4j.util.Strings;
 +import org.apache.logging.log4j.util.Supplier;
 +import org.junit.jupiter.api.BeforeEach;
 +import org.junit.jupiter.api.Test;
 +import org.junit.jupiter.api.parallel.ResourceLock;
 +
 +import java.util.Date;
 +import java.util.List;
 +import java.util.Locale;
 +import java.util.Properties;
 +
 +import static org.hamcrest.CoreMatchers.*;
 +import static org.hamcrest.MatcherAssert.assertThat;
 +import static org.junit.jupiter.api.Assertions.*;
 +
 +@ResourceLock("log4j2.MarkerManager")
 +@ResourceLock("log4j2.TestLogger")
 +@UsingThreadContextMap
 +public class LoggerTest {
 +
 +    private static class TestParameterizedMessageFactory {
 +        // empty
 +    }
 +
 +    private static class TestStringFormatterMessageFactory {
 +        // empty
 +    }
 +
 +    private final TestLogger logger = (TestLogger) LogManager.getLogger("LoggerTest");
 +    private final Marker marker = MarkerManager.getMarker("test");
 +    private final List<String> results = logger.getEntries();
 +
 +    @Test
 +    public void builder() {
 +        logger.atDebug().withLocation().log("Hello");
 +        logger.atError().withMarker(marker).log("Hello {}", "John");
 +        logger.atWarn().withThrowable(new Throwable("This is a test")).log((Message) new SimpleMessage("Log4j rocks!"));
 +        assertEquals(3, results.size());
 +        assertThat("Incorrect message 1", results.get(0),
 +                equalTo(" DEBUG org.apache.logging.log4j.LoggerTest.builder(LoggerTest.java:65) Hello"));
 +        assertThat("Incorrect message 2", results.get(1), equalTo("test ERROR Hello John"));
 +        assertThat("Incorrect message 3", results.get(2),
 +                startsWith(" WARN Log4j rocks! java.lang.Throwable: This is a test"));
 +        assertThat("Throwable incorrect in message 3", results.get(2),
 +                containsString("org.apache.logging.log4j.LoggerTest.builder(LoggerTest.java:67)"));
 +    }
 +
 +    @Test
 +    public void basicFlow() {
 +        logger.traceEntry();
 +        logger.traceExit();
 +        assertEquals(2, results.size());
 +        assertThat("Incorrect Entry", results.get(0), equalTo("ENTER[ FLOW ] TRACE Enter"));
 +        assertThat("incorrect Exit", results.get(1), equalTo("EXIT[ FLOW ] TRACE Exit"));
 +
 +    }
 +
 +    @Test
 +    public void flowTracingMessage() {
 +        Properties props = new Properties();
 +        props.setProperty("foo", "bar");
 +        logger.traceEntry(new JsonMessage(props));
 +        final Response response = new Response(-1, "Generic error");
 +        logger.traceExit(new JsonMessage(response),  response);
 +        assertEquals(2, results.size());
 +        assertThat("Incorrect Entry", results.get(0), startsWith("ENTER[ FLOW ] TRACE Enter"));
 +        assertThat("Missing entry data", results.get(0), containsString("\"foo\":\"bar\""));
 +        assertThat("incorrect Exit", results.get(1), startsWith("EXIT[ FLOW ] TRACE Exit"));
 +        assertThat("Missing exit data", results.get(1), containsString("\"message\":\"Generic error\""));
 +    }
 +
 +    @Test
 +    public void flowTracingString_ObjectArray1() {
 +        logger.traceEntry("doFoo(a={}, b={})", 1, 2);
 +        logger.traceExit("doFoo(a=1, b=2): {}", 3);
 +        assertEquals(2, results.size());
 +        assertThat("Incorrect Entry", results.get(0), startsWith("ENTER[ FLOW ] TRACE Enter"));
 +        assertThat("Missing entry data", results.get(0), containsString("doFoo(a=1, b=2)"));
 +        assertThat("Incorrect Exit", results.get(1), startsWith("EXIT[ FLOW ] TRACE Exit"));
 +        assertThat("Missing exit data", results.get(1), containsString("doFoo(a=1, b=2): 3"));
 +    }
 +
 +    @Test
 +    public void flowTracingExitValueOnly() {
 +        logger.traceEntry("doFoo(a={}, b={})", 1, 2);
 +        logger.traceExit(3);
 +        assertEquals(2, results.size());
 +        assertThat("Incorrect Entry", results.get(0), startsWith("ENTER[ FLOW ] TRACE Enter"));
 +        assertThat("Missing entry data", results.get(0), containsString("doFoo(a=1, b=2)"));
 +        assertThat("Incorrect Exit", results.get(1), startsWith("EXIT[ FLOW ] TRACE Exit"));
 +        assertThat("Missing exit data", results.get(1), containsString("3"));
 +    }
 +
 +    @Test
 +    public void flowTracingString_ObjectArray2() {
 +        final EntryMessage msg = logger.traceEntry("doFoo(a={}, b={})", 1, 2);
 +        logger.traceExit(msg, 3);
 +        assertEquals(2, results.size());
 +        assertThat("Incorrect Entry", results.get(0), startsWith("ENTER[ FLOW ] TRACE Enter"));
 +        assertThat("Missing entry data", results.get(0), containsString("doFoo(a=1, b=2)"));
 +        assertThat("Incorrect Exit", results.get(1), startsWith("EXIT[ FLOW ] TRACE Exit"));
 +        assertThat("Missing exit data", results.get(1), containsString("doFoo(a=1, b=2): 3"));
 +    }
 +
 +    @Test
 +    public void flowTracingVoidReturn() {
 +        final EntryMessage msg = logger.traceEntry("doFoo(a={}, b={})", 1, 2);
 +        logger.traceExit(msg);
 +        assertEquals(2, results.size());
 +        assertThat("Incorrect Entry", results.get(0), startsWith("ENTER[ FLOW ] TRACE Enter"));
 +        assertThat("Missing entry data", results.get(0), containsString("doFoo(a=1, b=2)"));
 +        assertThat("Incorrect Exit", results.get(1), startsWith("EXIT[ FLOW ] TRACE Exit"));
 +        assertThat("Missing exit data", results.get(1), endsWith("doFoo(a=1, b=2)"));
 +    }
 +
 +    @Test
 +    public void flowTracingNoExitArgs() {
 +        logger.traceEntry();
 +        logger.traceExit();
 +        assertEquals(2, results.size());
 +        assertThat("Incorrect Entry", results.get(0), startsWith("ENTER[ FLOW ] TRACE Enter"));
 +        assertThat("Incorrect Exit", results.get(1), startsWith("EXIT[ FLOW ] TRACE Exit"));
 +    }
 +
 +    @Test
 +    public void flowTracingNoArgs() {
 +        final EntryMessage message = logger.traceEntry();
 +        logger.traceExit(message);
 +        assertEquals(2, results.size());
 +        assertThat("Incorrect Entry", results.get(0), startsWith("ENTER[ FLOW ] TRACE Enter"));
 +        assertThat("Incorrect Exit", results.get(1), startsWith("EXIT[ FLOW ] TRACE Exit"));
 +    }
 +
 +    @Test
 +    public void flowTracingString_SupplierOfObjectMessages() {
 +        final EntryMessage msg = logger.traceEntry("doFoo(a={}, b={})", new Supplier<Message>() {
 +            @Override
 +            public Message get() {
 +                return new ObjectMessage(1);
 +            }
 +        }, new Supplier<Message>() {
 +            @Override
 +            public Message get() {
 +                return new ObjectMessage(2);
 +            }
 +        });
 +        logger.traceExit(msg, 3);
 +        assertEquals(2, results.size());
 +        assertThat("Incorrect Entry", results.get(0), startsWith("ENTER[ FLOW ] TRACE Enter"));
 +        assertThat("Missing entry data", results.get(0), containsString("doFoo(a=1, b=2)"));
 +        assertThat("Incorrect Exit", results.get(1), startsWith("EXIT[ FLOW ] TRACE Exit"));
 +        assertThat("Missing exit data", results.get(1), containsString("doFoo(a=1, b=2): 3"));
 +    }
 +
 +    @Test
 +    public void flowTracingString_SupplierOfStrings() {
 +        final EntryMessage msg = logger.traceEntry("doFoo(a={}, b={})", new Supplier<String>() {
 +            @Override
 +            public String get() {
 +                return "1";
 +            }
 +        }, new Supplier<String>() {
 +            @Override
 +            public String get() {
 +                return "2";
 +            }
 +        });
 +        logger.traceExit(msg, 3);
 +        assertEquals(2, results.size());
 +        assertThat("Incorrect Entry", results.get(0), startsWith("ENTER[ FLOW ] TRACE Enter"));
 +        assertThat("Missing entry data", results.get(0), containsString("doFoo(a=1, b=2)"));
 +        assertThat("Incorrect Exit", results.get(1), startsWith("EXIT[ FLOW ] TRACE Exit"));
 +        assertThat("Missing exit data", results.get(1), containsString("doFoo(a=1, b=2): 3"));
 +    }
 +
 +    @Test
 +    public void catching() {
 +        try {
 +            throw new NullPointerException();
 +        } catch (final Exception e) {
 +            logger.catching(e);
 +            assertEquals(1, results.size());
 +            assertThat("Incorrect Catching",
 +                    results.get(0), startsWith("CATCHING[ EXCEPTION ] ERROR Catching java.lang.NullPointerException"));
 +        }
 +    }
 +
 +    @Test
 +    public void debug() {
 +        logger.debug("Debug message");
 +        assertEquals(1, results.size());
 +        assertTrue(results.get(0).startsWith(" DEBUG Debug message"), "Incorrect message");
 +    }
 +
 +    @Test
 +    public void debugObject() {
 +        logger.debug(new Date());
 +        assertEquals(1, results.size());
 +        assertTrue(results.get(0).length() > 7, "Invalid length");
 +    }
 +
 +    @Test
 +    public void debugWithParms() {
 +        logger.debug("Hello, {}", "World");
 +        assertEquals(1, results.size());
 +        assertTrue(results.get(0).startsWith(" DEBUG Hello, World"), "Incorrect substitution");
 +    }
 +
 +    @Test
 +    public void debugWithParmsAndThrowable() {
 +        logger.debug("Hello, {}", "World", new RuntimeException("Test Exception"));
 +        assertEquals(1, results.size());
 +        assertTrue(
 +                results.get(0).startsWith(" DEBUG Hello, World java.lang.RuntimeException: Test Exception"),
 +                "Unexpected results: " + results.get(0));
 +    }
 +
 +    @Test
 +    public void getFormatterLogger() {
 +        // The TestLogger logger was already created in an instance variable for this class.
 +        // The message factory is only used when the logger is created.
 +        final TestLogger testLogger = (TestLogger) LogManager.getFormatterLogger();
 +        final TestLogger altLogger = (TestLogger) LogManager.getFormatterLogger(getClass());
 +        assertEquals(testLogger.getName(), altLogger.getName());
 +        assertNotNull(testLogger);
 +        assertMessageFactoryInstanceOf(testLogger.getMessageFactory(), StringFormatterMessageFactory.class);
 +        assertEqualMessageFactory(StringFormatterMessageFactory.INSTANCE, testLogger);
 +        testLogger.debug("%,d", Integer.MAX_VALUE);
 +        assertEquals(1, testLogger.getEntries().size());
 +        assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
 +    }
 +
 +    @Test
 +    public void getFormatterLogger_Class() {
 +        // The TestLogger logger was already created in an instance variable for this class.
 +        // The message factory is only used when the logger is created.
 +        final TestLogger testLogger = (TestLogger) LogManager.getFormatterLogger(TestStringFormatterMessageFactory.class);
 +        assertNotNull(testLogger);
 +        assertMessageFactoryInstanceOf(testLogger.getMessageFactory(), StringFormatterMessageFactory.class);
 +        assertEqualMessageFactory(StringFormatterMessageFactory.INSTANCE, testLogger);
 +        testLogger.debug("%,d", Integer.MAX_VALUE);
 +        assertEquals(1, testLogger.getEntries().size());
 +        assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
 +    }
 +
 +    private static void assertMessageFactoryInstanceOf(MessageFactory factory, final Class<?> cls) {
 +        assertTrue(factory.getClass().isAssignableFrom(cls));
 +    }
 +
 +    @Test
 +    public void getFormatterLogger_Object() {
 +        // The TestLogger logger was already created in an instance variable for this class.
 +        // The message factory is only used when the logger is created.
 +        final TestLogger testLogger = (TestLogger) LogManager.getFormatterLogger(new TestStringFormatterMessageFactory());
 +        assertNotNull(testLogger);
 +        assertMessageFactoryInstanceOf(testLogger.getMessageFactory(), StringFormatterMessageFactory.class);
 +        assertEqualMessageFactory(StringFormatterMessageFactory.INSTANCE, testLogger);
 +        testLogger.debug("%,d", Integer.MAX_VALUE);
 +        assertEquals(1, testLogger.getEntries().size());
 +        assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
 +    }
 +
 +    @Test
 +    public void getFormatterLogger_String() {
 +        final StringFormatterMessageFactory messageFactory = StringFormatterMessageFactory.INSTANCE;
 +        final TestLogger testLogger = (TestLogger) LogManager.getFormatterLogger("getLogger_String_StringFormatterMessageFactory");
 +        assertNotNull(testLogger);
 +        assertMessageFactoryInstanceOf(testLogger.getMessageFactory(), StringFormatterMessageFactory.class);
 +        assertEqualMessageFactory(messageFactory, testLogger);
 +        testLogger.debug("%,d", Integer.MAX_VALUE);
 +        assertEquals(1, testLogger.getEntries().size());
 +        assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
 +    }
 +
 +    @Test
 +    public void getLogger_Class_ParameterizedMessageFactory() {
 +        // The TestLogger logger was already created in an instance variable for this class.
 +        // The message factory is only used when the logger is created.
 +        final ParameterizedMessageFactory messageFactory = ParameterizedMessageFactory.INSTANCE;
 +        final TestLogger testLogger = (TestLogger) LogManager.getLogger(TestParameterizedMessageFactory.class,
 +                messageFactory);
 +        assertNotNull(testLogger);
 +        assertEqualMessageFactory(messageFactory, testLogger);
 +        testLogger.debug("{}", Integer.MAX_VALUE);
 +        assertEquals(1, testLogger.getEntries().size());
 +        assertEquals(" DEBUG " + Integer.MAX_VALUE, testLogger.getEntries().get(0));
 +    }
 +
 +    @Test
 +    public void getLogger_Class_StringFormatterMessageFactory() {
 +        // The TestLogger logger was already created in an instance variable for this class.
 +        // The message factory is only used when the logger is created.
 +        final TestLogger testLogger = (TestLogger) LogManager.getLogger(TestStringFormatterMessageFactory.class,
 +                StringFormatterMessageFactory.INSTANCE);
 +        assertNotNull(testLogger);
 +        assertEqualMessageFactory(StringFormatterMessageFactory.INSTANCE, testLogger);
 +        testLogger.debug("%,d", Integer.MAX_VALUE);
 +        assertEquals(1, testLogger.getEntries().size());
 +        assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
 +    }
 +
 +    @Test
 +    public void getLogger_Object_ParameterizedMessageFactory() {
 +        // The TestLogger logger was already created in an instance variable for this class.
 +        // The message factory is only used when the logger is created.
 +        final ParameterizedMessageFactory messageFactory =  ParameterizedMessageFactory.INSTANCE;
 +        final TestLogger testLogger = (TestLogger) LogManager.getLogger(new TestParameterizedMessageFactory(),
 +                messageFactory);
 +        assertNotNull(testLogger);
 +        assertEqualMessageFactory(messageFactory, testLogger);
 +        testLogger.debug("{}", Integer.MAX_VALUE);
 +        assertEquals(1, testLogger.getEntries().size());
 +        assertEquals(" DEBUG " + Integer.MAX_VALUE, testLogger.getEntries().get(0));
 +    }
 +
 +    private void assertEqualMessageFactory(final MessageFactory messageFactory, final TestLogger testLogger) {
 +        MessageFactory actual = testLogger.getMessageFactory();
 +        assertEquals(messageFactory, actual);
 +    }
 +
 +    @Test
 +    public void getLogger_Object_StringFormatterMessageFactory() {
 +        // The TestLogger logger was already created in an instance variable for this class.
 +        // The message factory is only used when the logger is created.
 +        final StringFormatterMessageFactory messageFactory = StringFormatterMessageFactory.INSTANCE;
 +        final TestLogger testLogger = (TestLogger) LogManager.getLogger(new TestStringFormatterMessageFactory(),
 +                messageFactory);
 +        assertNotNull(testLogger);
 +        assertEqualMessageFactory(messageFactory, testLogger);
 +        testLogger.debug("%,d", Integer.MAX_VALUE);
 +        assertEquals(1, testLogger.getEntries().size());
 +        assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
 +    }
 +
 +    @Test
 +    public void getLogger_String_MessageFactoryMismatch() {
 +        final StringFormatterMessageFactory messageFactory = StringFormatterMessageFactory.INSTANCE;
 +        final TestLogger testLogger = (TestLogger) LogManager.getLogger("getLogger_String_MessageFactoryMismatch",
 +                messageFactory);
 +        assertNotNull(testLogger);
 +        assertEqualMessageFactory(messageFactory, testLogger);
 +        final TestLogger testLogger2 = (TestLogger) LogManager.getLogger("getLogger_String_MessageFactoryMismatch",
 +                ParameterizedMessageFactory.INSTANCE);
 +        assertNotNull(testLogger2);
 +        //TODO: How to test?
 +        //This test context always creates new loggers, other test context impls I tried fail other tests.
 +        //assertEquals(messageFactory, testLogger2.getMessageFactory());
 +        testLogger.debug("%,d", Integer.MAX_VALUE);
 +        assertEquals(1, testLogger.getEntries().size());
 +        assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
 +    }
 +
 +    @Test
 +    public void getLogger_String_ParameterizedMessageFactory() {
 +        final ParameterizedMessageFactory messageFactory =  ParameterizedMessageFactory.INSTANCE;
 +        final TestLogger testLogger = (TestLogger) LogManager.getLogger("getLogger_String_ParameterizedMessageFactory",
 +                messageFactory);
 +        assertNotNull(testLogger);
 +        assertEqualMessageFactory(messageFactory, testLogger);
 +        testLogger.debug("{}", Integer.MAX_VALUE);
 +        assertEquals(1, testLogger.getEntries().size());
 +        assertEquals(" DEBUG " + Integer.MAX_VALUE, testLogger.getEntries().get(0));
 +    }
 +
 +    @Test
 +    public void getLogger_String_SimpleMessageFactory() {
 +        final SimpleMessageFactory messageFactory = SimpleMessageFactory.INSTANCE;
 +        final TestLogger testLogger = (TestLogger) LogManager.getLogger("getLogger_String_StringFormatterMessageFactory",
 +                messageFactory);
 +        assertNotNull(testLogger);
 +        assertEqualMessageFactory(messageFactory, testLogger);
 +        testLogger.debug("{} %,d {foo}", Integer.MAX_VALUE);
 +        assertEquals(1, testLogger.getEntries().size());
 +        assertEquals(" DEBUG {} %,d {foo}", testLogger.getEntries().get(0));
 +    }
 +
 +    @Test
 +    public void getLogger_String_StringFormatterMessageFactory() {
 +        final StringFormatterMessageFactory messageFactory = StringFormatterMessageFactory.INSTANCE;
 +        final TestLogger testLogger = (TestLogger) LogManager.getLogger("getLogger_String_StringFormatterMessageFactory",
 +                messageFactory);
 +        assertNotNull(testLogger);
 +        assertEqualMessageFactory(messageFactory, testLogger);
 +        testLogger.debug("%,d", Integer.MAX_VALUE);
 +        assertEquals(1, testLogger.getEntries().size());
 +        assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
 +    }
 +
 +    @Test
 +    public void getLoggerByClass() {
 +        final Logger classLogger = LogManager.getLogger(LoggerTest.class);
 +        assertNotNull(classLogger);
 +    }
 +
 +    @Test
 +    public void getLoggerByNullClass() {
 +        // Returns a SimpleLogger
 +        assertNotNull(LogManager.getLogger((Class<?>) null));
 +    }
 +
 +    @Test
 +    public void getLoggerByNullObject() {
 +        // Returns a SimpleLogger
 +        assertNotNull(LogManager.getLogger((Object) null));
 +    }
 +
 +    @Test
 +    public void getLoggerByNullString() {
 +        // Returns a SimpleLogger
 +        assertNotNull(LogManager.getLogger((String) null));
 +    }
 +
 +    @Test
 +    public void getLoggerByObject() {
 +        final Logger classLogger = LogManager.getLogger(this);
 +        assertNotNull(classLogger);
 +        assertEquals(classLogger, LogManager.getLogger(LoggerTest.class));
 +    }
 +
 +    @Test
 +    public void getRootLogger() {
 +        assertNotNull(LogManager.getRootLogger());
 +        assertNotNull(LogManager.getLogger(Strings.EMPTY));
 +        assertNotNull(LogManager.getLogger(LogManager.ROOT_LOGGER_NAME));
 +        assertEquals(LogManager.getRootLogger(), LogManager.getLogger(Strings.EMPTY));
 +        assertEquals(LogManager.getRootLogger(), LogManager.getLogger(LogManager.ROOT_LOGGER_NAME));
 +    }
 +
 +    @Test
 +    public void isAllEnabled() {
 +        assertTrue(logger.isEnabled(Level.ALL), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void isDebugEnabled() {
 +        assertTrue(logger.isDebugEnabled(), "Incorrect level");
 +        assertTrue(logger.isEnabled(Level.DEBUG), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void isErrorEnabled() {
 +        assertTrue(logger.isErrorEnabled(), "Incorrect level");
 +        assertTrue(logger.isEnabled(Level.ERROR), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void isFatalEnabled() {
 +        assertTrue(logger.isFatalEnabled(), "Incorrect level");
 +        assertTrue(logger.isEnabled(Level.FATAL), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void isInfoEnabled() {
 +        assertTrue(logger.isInfoEnabled(), "Incorrect level");
 +        assertTrue(logger.isEnabled(Level.INFO), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void isOffEnabled() {
 +        assertTrue(logger.isEnabled(Level.OFF), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void isTraceEnabled() {
 +        assertTrue(logger.isTraceEnabled(), "Incorrect level");
 +        assertTrue(logger.isEnabled(Level.TRACE), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void isWarnEnabled() {
 +        assertTrue(logger.isWarnEnabled(), "Incorrect level");
 +        assertTrue(logger.isEnabled(Level.WARN), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void isAllEnabledWithMarker() {
 +        assertTrue(logger.isEnabled(Level.ALL, marker), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void isDebugEnabledWithMarker() {
 +        assertTrue(logger.isDebugEnabled(marker), "Incorrect level");
 +        assertTrue(logger.isEnabled(Level.DEBUG, marker), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void isErrorEnabledWithMarker() {
 +        assertTrue(logger.isErrorEnabled(marker), "Incorrect level");
 +        assertTrue(logger.isEnabled(Level.ERROR, marker), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void isFatalEnabledWithMarker() {
 +        assertTrue(logger.isFatalEnabled(marker), "Incorrect level");
 +        assertTrue(logger.isEnabled(Level.FATAL, marker), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void isInfoEnabledWithMarker() {
 +        assertTrue(logger.isInfoEnabled(marker), "Incorrect level");
 +        assertTrue(logger.isEnabled(Level.INFO, marker), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void isOffEnabledWithMarker() {
 +        assertTrue(logger.isEnabled(Level.OFF, marker), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void isTraceEnabledWithMarker() {
 +        assertTrue(logger.isTraceEnabled(marker), "Incorrect level");
 +        assertTrue(logger.isEnabled(Level.TRACE, marker), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void isWarnEnabledWithMarker() {
 +        assertTrue(logger.isWarnEnabled(marker), "Incorrect level");
 +        assertTrue(logger.isEnabled(Level.WARN, marker), "Incorrect level");
 +    }
 +
 +    @Test
 +    public void mdc() {
-         ThreadContext.clearAll();
++        ThreadContext.clearMap();
 +        ThreadContext.put("TestYear", Integer.valueOf(2010).toString());
 +        logger.debug("Debug message");
 +        String testYear = ThreadContext.get("TestYear");
 +        assertNotNull(testYear, "Test Year is null");
 +        assertEquals("2010", testYear, "Incorrect test year: " + testYear);
 +        ThreadContext.clearMap();
 +        logger.debug("Debug message");
 +        assertEquals(2, results.size());
 +        System.out.println("Log line 1: " + results.get(0));
 +        System.out.println("log line 2: " + results.get(1));
 +        assertTrue(
 +                results.get(0).startsWith(" DEBUG Debug message {TestYear=2010}"), "Incorrect MDC: " + results.get(0));
 +        assertTrue(
 +                results.get(1).startsWith(" DEBUG Debug message"), "MDC not cleared?: " + results.get(1));
 +    }
 +
 +    @Test
 +    public void printf() {
 +        logger.printf(Level.DEBUG, "Debug message %d", 1);
 +        logger.printf(Level.DEBUG, MarkerManager.getMarker("Test"), "Debug message %d", 2);
 +        assertEquals(2, results.size());
 +        assertThat("Incorrect message", results.get(0), startsWith(" DEBUG Debug message 1"));
 +        assertThat("Incorrect message", results.get(1), startsWith("Test DEBUG Debug message 2"));
 +    }
 +
 +    @BeforeEach
 +    public void setup() {
 +        results.clear();
 +    }
 +
 +    @Test
 +    public void structuredData() {
 +        ThreadContext.put("loginId", "JohnDoe");
 +        ThreadContext.put("ipAddress", "192.168.0.120");
 +        ThreadContext.put("locale", Locale.US.getDisplayName());
 +        final StructuredDataMessage msg = new StructuredDataMessage("Audit@18060", "Transfer Complete", "Transfer");
 +        msg.put("ToAccount", "123456");
 +        msg.put("FromAccount", "123457");
 +        msg.put("Amount", "200.00");
 +        logger.info(MarkerManager.getMarker("EVENT"), msg);
 +        ThreadContext.clearMap();
 +        assertEquals(1, results.size());
 +        assertThat("Incorrect structured data: ", results.get(0), startsWith(
 +                "EVENT INFO Transfer [Audit@18060 Amount=\"200.00\" FromAccount=\"123457\" ToAccount=\"123456\"] Transfer Complete"));
 +    }
 +
 +    @Test
 +    public void throwing() {
 +        logger.throwing(new IllegalArgumentException("Test Exception"));
 +        assertEquals(1, results.size());
 +        assertThat("Incorrect Throwing",
 +                results.get(0), startsWith("THROWING[ EXCEPTION ] ERROR Throwing java.lang.IllegalArgumentException: Test Exception"));
 +    }
 +
 +
 +    private static class Response {
 +        int status;
 +        String message;
 +
 +        public Response(final int status, final String message) {
 +            this.status = status;
 +            this.message = message;
 +        }
 +
 +        public int getStatus() {
 +            return status;
 +        }
 +
 +        public void setStatus(final int status) {
 +            this.status = status;
 +        }
 +
 +        public String getMessage() {
 +            return message;
 +        }
 +
 +        public void setMessage(final String message) {
 +            this.message = message;
 +        }
 +    }
 +}
diff --cc log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
index 5c79106,0000000..6a38598
mode 100644,000000..100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
@@@ -1,59 -1,0 +1,61 @@@
 +/*
 + * 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;
 +
++import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
 +import org.junit.jupiter.api.AfterAll;
 +import org.junit.jupiter.api.BeforeAll;
 +import org.junit.jupiter.api.Test;
 +import org.junit.jupiter.api.parallel.ResourceLock;
 +import org.junit.jupiter.api.parallel.Resources;
 +
 +import static org.junit.jupiter.api.Assertions.assertNull;
 +
 +/**
 + * Tests {@link ThreadContext}.
 + */
 +@ResourceLock(Resources.SYSTEM_PROPERTIES)
++@UsingThreadContextMap
 +public class NoopThreadContextTest {
 +
 +    private static final String TRUE = "true";
 +    private static final String PROPERY_KEY_ALL = "disableThreadContext";
 +    private static final String PROPERY_KEY_MAP = "disableThreadContextMap";
 +
 +    @BeforeAll
 +    public static void before() {
 +        System.setProperty(PROPERY_KEY_ALL, TRUE);
 +        System.setProperty(PROPERY_KEY_MAP, TRUE);
 +        ThreadContext.init();
 +    }
 +
 +    @AfterAll
 +    public static void after() {
 +        System.clearProperty(PROPERY_KEY_ALL);
 +        System.clearProperty(PROPERY_KEY_MAP);
 +        ThreadContext.init();
 +    }
 +
 +    @Test
 +    public void testNoop() {
 +        ThreadContext.put("Test", "Test");
 +        final String value = ThreadContext.get("Test");
 +        assertNull(value, "value was saved");
 +    }
 +
 +
 +}
diff --cc log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/di/BeanManagerTest.java
index 0000000,3f67266..3f67266
mode 000000,100644..100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/di/BeanManagerTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/di/BeanManagerTest.java
diff --cc log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/di/InjectionPointTest.java
index 0000000,60e89e4..60e89e4
mode 000000,100644..100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/di/InjectionPointTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/di/InjectionPointTest.java
diff --cc log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/di/InjectionTargetTest.java
index 0000000,c06d2ad..c06d2ad
mode 000000,100644..100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/di/InjectionTargetTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/di/InjectionTargetTest.java
diff --cc log4j-core-test/src/test/java9/module-info.java
index c427d5c,0000000..d02fec4
mode 100644,000000..100644
--- a/log4j-core-test/src/test/java9/module-info.java
+++ b/log4j-core-test/src/test/java9/module-info.java
@@@ -1,92 -1,0 +1,94 @@@
 +/*
 + * 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.
 + */
 +open module org.apache.logging.log4j.core {
 +    exports org.apache.logging.log4j.core;
 +    exports org.apache.logging.log4j.core.appender;
 +    exports org.apache.logging.log4j.core.appender.db;
 +    exports org.apache.logging.log4j.core.appender.nosql;
 +    exports org.apache.logging.log4j.core.appender.rewrite;
 +    exports org.apache.logging.log4j.core.appender.rolling;
 +    exports org.apache.logging.log4j.core.appender.rolling.action;
 +    exports org.apache.logging.log4j.core.appender.routing;
 +    exports org.apache.logging.log4j.core.async;
 +    exports org.apache.logging.log4j.core.config;
 +    exports org.apache.logging.log4j.core.config.arbiters;
 +    exports org.apache.logging.log4j.core.config.builder;
++    exports org.apache.logging.log4j.core.config.di;
++    exports org.apache.logging.log4j.core.config.di.impl;
 +    exports org.apache.logging.log4j.core.config.plugins;
 +    exports org.apache.logging.log4j.core.config.plugins.convert;
 +    exports org.apache.logging.log4j.core.config.plugins.util;
 +    exports org.apache.logging.log4j.core.config.plugins.validation.validators;
 +    exports org.apache.logging.log4j.core.config.properties;
 +    exports org.apache.logging.log4j.core.config.xml;
 +    exports org.apache.logging.log4j.core.filter;
 +    exports org.apache.logging.log4j.core.impl;
 +    exports org.apache.logging.log4j.core.jmx;
 +    exports org.apache.logging.log4j.core.layout;
 +    exports org.apache.logging.log4j.core.lookup;
 +    exports org.apache.logging.log4j.core.message;
 +    exports org.apache.logging.log4j.core.net;
 +    exports org.apache.logging.log4j.core.net.ssl;
 +    exports org.apache.logging.log4j.core.parser;
 +    exports org.apache.logging.log4j.core.pattern;
 +    exports org.apache.logging.log4j.core.script;
 +    exports org.apache.logging.log4j.core.selector;
 +    exports org.apache.logging.log4j.core.test;
 +    exports org.apache.logging.log4j.core.test.appender;
 +    exports org.apache.logging.log4j.core.test.hamcrest;
 +    exports org.apache.logging.log4j.core.test.junit;
 +    exports org.apache.logging.log4j.core.time;
 +    exports org.apache.logging.log4j.core.tools;
 +    exports org.apache.logging.log4j.core.util;
 +
 +    requires transitive java.compiler;
 +    requires transitive java.desktop;
 +    requires transitive java.management;
 +    requires transitive java.sql;
 +    requires transitive java.rmi;
 +    requires transitive java.scripting;
 +    requires transitive java.xml;
 +    requires transitive org.apache.logging.log4j;
 +    requires transitive org.apache.logging.log4j.test;
 +    requires transitive org.apache.logging.log4j.plugins;
 +    requires transitive org.apache.logging.log4j.plugins.test;
 +    requires transitive com.lmax.disruptor;
 +    requires transitive org.jctools.core;
 +    requires transitive org.osgi.framework;
 +    requires transitive com.conversantmedia.disruptor;
 +    requires transitive net.bytebuddy;
 +    requires com.fasterxml.jackson.core;
 +    requires com.fasterxml.jackson.databind;
 +    requires transitive com.fasterxml.jackson.dataformat.xml;
 +    requires transitive com.fasterxml.jackson.dataformat.yaml;
 +    requires transitive org.apache.commons.compress;
 +    requires transitive org.fusesource.jansi;
 +    requires transitive org.junit.jupiter.api;
 +    requires transitive org.junit.jupiter.engine;
 +    requires transitive org.junit.jupiter.params;
 +    requires transitive org.junit.platform.commons;
 +    requires transitive org.junit.platform.engine;
 +    requires transitive junit;
 +
 +    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,
 +            org.apache.logging.log4j.core.test.plugins.Log4jPlugins;
 +}
diff --cc log4j-core/src/main/java/module-info.java
index 9133d77,0000000..d4b3d93
mode 100644,000000..100644
--- a/log4j-core/src/main/java/module-info.java
+++ b/log4j-core/src/main/java/module-info.java
@@@ -1,86 -1,0 +1,88 @@@
 +/*
 + * 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.
 + */
 +module org.apache.logging.log4j.core {
 +    exports org.apache.logging.log4j.core;
 +    exports org.apache.logging.log4j.core.appender;
 +    exports org.apache.logging.log4j.core.appender.db;
 +    exports org.apache.logging.log4j.core.appender.nosql;
 +    exports org.apache.logging.log4j.core.appender.rewrite;
 +    exports org.apache.logging.log4j.core.appender.rolling;
 +    exports org.apache.logging.log4j.core.appender.rolling.action;
 +    exports org.apache.logging.log4j.core.appender.routing;
 +    exports org.apache.logging.log4j.core.async;
 +    exports org.apache.logging.log4j.core.config;
 +    exports org.apache.logging.log4j.core.config.arbiters;
 +    exports org.apache.logging.log4j.core.config.builder.api;
 +    exports org.apache.logging.log4j.core.config.builder.impl;
 +    exports org.apache.logging.log4j.core.config.composite;
++    exports org.apache.logging.log4j.core.config.di;
++    exports org.apache.logging.log4j.core.config.di.impl;
 +    exports org.apache.logging.log4j.core.config.json;
 +    exports org.apache.logging.log4j.core.config.plugins;
 +    exports org.apache.logging.log4j.core.config.plugins.convert;
 +    exports org.apache.logging.log4j.core.config.plugins.inject;
 +    exports org.apache.logging.log4j.core.config.plugins.util;
 +    exports org.apache.logging.log4j.core.config.plugins.visitors;
 +    exports org.apache.logging.log4j.core.config.properties;
 +    exports org.apache.logging.log4j.core.config.status;
 +    exports org.apache.logging.log4j.core.config.xml;
 +    exports org.apache.logging.log4j.core.config.yaml;
 +    exports org.apache.logging.log4j.core.filter;
 +    exports org.apache.logging.log4j.core.impl;
 +    exports org.apache.logging.log4j.core.jmx;
 +    exports org.apache.logging.log4j.core.layout;
 +    exports org.apache.logging.log4j.core.lookup;
 +    exports org.apache.logging.log4j.core.message;
 +    exports org.apache.logging.log4j.core.net;
 +    exports org.apache.logging.log4j.core.net.ssl;
 +    exports org.apache.logging.log4j.core.osgi;
 +    exports org.apache.logging.log4j.core.parser;
 +    exports org.apache.logging.log4j.core.pattern;
 +    exports org.apache.logging.log4j.core.script;
 +    exports org.apache.logging.log4j.core.selector;
 +    exports org.apache.logging.log4j.core.time;
 +    exports org.apache.logging.log4j.core.tools;
 +    exports org.apache.logging.log4j.core.tools.picocli;
 +    exports org.apache.logging.log4j.core.util;
 +    exports org.apache.logging.log4j.core.util.datetime;
 +
 +    requires transitive java.desktop;
 +    requires transitive java.management;
 +    requires transitive java.naming;
 +    requires transitive java.sql;
 +    requires transitive java.rmi;
 +    requires transitive java.scripting;
 +    requires transitive java.xml;
 +    requires transitive org.apache.logging.log4j;
 +    requires transitive org.apache.logging.log4j.plugins;
 +    requires transitive com.lmax.disruptor;
 +    requires transitive org.jctools.core;
 +    requires transitive org.osgi.framework;
 +    requires transitive com.conversantmedia.disruptor;
 +    requires transitive com.fasterxml.jackson.core;
 +    requires transitive com.fasterxml.jackson.databind;
 +    requires transitive com.fasterxml.jackson.dataformat.xml;
 +    requires transitive com.fasterxml.jackson.dataformat.yaml;
 +    requires transitive org.apache.commons.compress;
 +    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;
 +}
diff --cc log4j-plugin-processor/src/main/java/org/apache/logging/log4j/plugin/processor/BeanProcessor.java
index 0000000,0000000..3ab23ca
new file mode 100644
--- /dev/null
+++ b/log4j-plugin-processor/src/main/java/org/apache/logging/log4j/plugin/processor/BeanProcessor.java
@@@ -1,0 -1,0 +1,516 @@@
++/*
++ * 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.plugin.processor;
++
++import org.apache.logging.log4j.plugins.di.DependentScoped;
++import org.apache.logging.log4j.plugins.di.Disposes;
++import org.apache.logging.log4j.plugins.di.Inject;
++import org.apache.logging.log4j.plugins.di.Producer;
++import org.apache.logging.log4j.plugins.di.Qualifier;
++import org.apache.logging.log4j.plugins.di.ScopeType;
++
++import javax.annotation.processing.AbstractProcessor;
++import javax.annotation.processing.RoundEnvironment;
++import javax.annotation.processing.SupportedAnnotationTypes;
++import javax.annotation.processing.SupportedOptions;
++import javax.lang.model.SourceVersion;
++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.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.ArrayList;
++import java.util.Comparator;
++import java.util.HashSet;
++import java.util.LinkedHashSet;
++import java.util.List;
++import java.util.Set;
++import java.util.function.Predicate;
++import java.util.stream.Collectors;
++import java.util.stream.Stream;
++
++@SupportedAnnotationTypes({"org.apache.logging.log4j.plugins.*", "org.apache.logging.log4j.core.config.plugins.*"})
++@SupportedOptions({"pluginPackage", "pluginClassName"})
++public class BeanProcessor extends AbstractProcessor {
++    public static final String PLUGIN_MODULE_SERVICE_FILE = "META-INF/services/org.apache.logging.log4j.plugins.di.model.PluginModule";
++
++    public BeanProcessor() {
++    }
++
++    @Override
++    public SourceVersion getSupportedSourceVersion() {
++        return SourceVersion.latestSupported();
++    }
++
++    private static class ProducerAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
++        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(new ProducerFieldMirror(e));
++            return null;
++        }
++
++        @Override
++        public Void visitExecutableAsMethod(final ExecutableElement e, final Void unused) {
++            producerMethods.add(new ProducerMethodMirror(e));
++            return null;
++        }
++    }
++
++    private static class DisposesAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
++        private final List<DisposesMirror> disposesParameters = new ArrayList<>();
++
++        @Override
++        public Void visitVariableAsParameter(final VariableElement e, final Void unused) {
++            disposesParameters.add(new DisposesMirror(e));
++            return null;
++        }
++    }
++
++    private static class InjectAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
++        private final List<InjectionTargetMirror> injectableClasses = new ArrayList<>();
++
++        @Override
++        public Void visitVariableAsField(final VariableElement e, final Void unused) {
++            injectableClasses.add(new InjectionTargetMirror(((TypeElement) e.getEnclosingElement())));
++            return null;
++        }
++
++        @Override
++        public Void visitExecutableAsConstructor(final ExecutableElement e, final Void unused) {
++            injectableClasses.add(new InjectionTargetMirror((TypeElement) e.getEnclosingElement()));
++            return null;
++        }
++
++        @Override
++        public Void visitExecutableAsMethod(final ExecutableElement e, final Void unused) {
++            injectableClasses.add(new InjectionTargetMirror((TypeElement) e.getEnclosingElement()));
++            return null;
++        }
++    }
++
++    private static class QualifiedAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
++        private final Predicate<AnnotationMirror> isProducerAnnotation;
++        private final List<InjectionTargetMirror> injectableClasses = new ArrayList<>();
++
++        private QualifiedAnnotationVisitor(final Predicate<AnnotationMirror> isProducerAnnotation) {
++            this.isProducerAnnotation = isProducerAnnotation;
++        }
++
++        @Override
++        public Void visitVariableAsField(final VariableElement e, final Void unused) {
++            if (e.getAnnotationMirrors().stream().noneMatch(isProducerAnnotation)) {
++                injectableClasses.add(new InjectionTargetMirror((TypeElement) e.getEnclosingElement()));
++            }
++            return null;
++        }
++
++        @Override
++        public Void visitVariableAsParameter(final VariableElement e, final Void unused) {
++            final Element enclosingExecutable = e.getEnclosingElement();
++            final TypeElement typeElement = (TypeElement) enclosingExecutable.getEnclosingElement();
++            if (enclosingExecutable.getKind() == ElementKind.CONSTRUCTOR ||
++                    enclosingExecutable.getAnnotationMirrors().stream().noneMatch(isProducerAnnotation)) {
++                injectableClasses.add(new InjectionTargetMirror(typeElement));
++            }
++            return null;
++        }
++    }
++
++    private static class PluginAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
++        private final List<GenericPluginMirror> plugins = new ArrayList<>();
++
++        @Override
++        public Void visitTypeAsClass(final TypeElement e, final Void unused) {
++            plugins.add(new GenericPluginMirror(e));
++            return null;
++        }
++    }
++
++    private static class ScopeTypeVisitor extends ElementKindVisitor9<TypeElement, Types> {
++        protected ScopeTypeVisitor(final TypeElement defaultValue) {
++            super(defaultValue);
++        }
++
++        @Override
++        public TypeElement visitType(final TypeElement e, final Types types) {
++            for (final AnnotationMirror annotationMirror : e.getAnnotationMirrors()) {
++                final DeclaredType annotationType = annotationMirror.getAnnotationType();
++                if (annotationType.getAnnotation(ScopeType.class) != null) {
++                    return (TypeElement) annotationType.asElement();
++                }
++            }
++            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()) {
++            return false;
++        }
++
++        final TypeElement[] producerAnnotations = annotations.stream()
++                .filter(e -> e.getAnnotation(Producer.class) != null)
++                .toArray(TypeElement[]::new);
++        final var producesAnnotationVisitor = new ProducerAnnotationVisitor();
++        roundEnv.getElementsAnnotatedWithAny(producerAnnotations).forEach(producesAnnotationVisitor::visit);
++
++        final var disposesAnnotationVisitor = new DisposesAnnotationVisitor();
++        roundEnv.getElementsAnnotatedWith(Disposes.class).forEach(disposesAnnotationVisitor::visit);
++
++        final var injectAnnotationVisitor = new InjectAnnotationVisitor();
++        roundEnv.getElementsAnnotatedWith(Inject.class).forEach(injectAnnotationVisitor::visit);
++
++        final Types types = processingEnv.getTypeUtils();
++        final var qualifiedAnnotationVisitor = new QualifiedAnnotationVisitor(annotationMirror -> {
++            for (final TypeElement producerAnnotation : producerAnnotations) {
++                if (types.isSameType(producerAnnotation.asType(), annotationMirror.getAnnotationType())) {
++                    return true;
++                }
++            }
++            return false;
++        });
++        final TypeElement[] qualifierAnnotations = annotations.stream()
++                .filter(e -> e.getAnnotation(Qualifier.class) != null)
++                .toArray(TypeElement[]::new);
++        roundEnv.getElementsAnnotatedWithAny(qualifierAnnotations).forEach(qualifiedAnnotationVisitor::visit);
++
++        final TypeElement[] pluginAnnotations = annotations.stream()
++                .filter(e -> e.getSimpleName().contentEquals("Plugin"))
++                .toArray(TypeElement[]::new);
++        final var pluginAnnotationVisitor = new PluginAnnotationVisitor();
++        roundEnv.getElementsAnnotatedWithAny(pluginAnnotations).forEach(pluginAnnotationVisitor::visit);
++
++        final Set<PackageElement> packageElements = new HashSet<>();
++        final Set<TypeElement> declaringTypes = new HashSet<>();
++
++        final Elements elements = processingEnv.getElementUtils();
++        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) {
++            packageName = packageElements.stream()
++                    .map(PackageElement::getQualifiedName)
++                    .map(CharSequence.class::cast)
++                    .reduce(BeanProcessor::commonPrefix)
++                    .orElseThrow()
++                    .toString();
++        }
++        String className = processingEnv.getOptions().getOrDefault("pluginClassName", "Log4jModule");
++        try {
++            writePluginModule(packageName, className, mirrors);
++            return false;
++        } catch (IOException e) {
++            throw new UncheckedIOException(e);
++        }
++    }
++
++    private void writePluginModule(final CharSequence packageName, final CharSequence className,
++                                   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);
++        }
++        try (final PrintWriter out = new PrintWriter(processingEnv.getFiler().createSourceFile(
++                packageName + ".plugins." + className).openWriter())) {
++            out.println("package " + packageName + ".plugins;");
++            out.println();
++            out.println("import org.apache.logging.log4j.plugins.di.model.*;");
++            out.println();
++            out.println("import java.util.List;");
++            out.println("import java.util.Set;");
++            out.println();
++            out.println("public class " + className + " extends PluginModule {");
++            out.println();
++            out.println("  private static final List<PluginSource> PLUGINS = List.of(" + javaListOfPlugins(mirrors) + ");");
++            out.println();
++            out.println("  public " + className + "() {");
++            out.println("    super(PLUGINS);");
++            out.println("  }");
++            out.println();
++            out.println("}");
++        }
++    }
++
++    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 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 Set<Name> getImplementedInterfaces(final TypeMirror base) {
++        final Set<Name> implementedInterfaces = new LinkedHashSet<>();
++        final Types types = processingEnv.getTypeUtils();
++        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);
++                }
++                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) {
++        final int minLength = Math.min(str1.length(), str2.length());
++        for (int i = 0; i < minLength; i++) {
++            if (str1.charAt(i) != str2.charAt(i)) {
++                if (i > 1 && str1.charAt(i - 1) == '.') {
++                    return str1.subSequence(0, i - 1);
++                } else {
++                    return str1.subSequence(0, i);
++                }
++            }
++        }
++        return str1.subSequence(0, minLength);
++    }
++
++}
diff --cc log4j-plugin-processor/src/main/java/org/apache/logging/log4j/plugin/processor/PluginProcessor.java
index df16f58,0000000..bd3d693
mode 100644,000000..100644
--- a/log4j-plugin-processor/src/main/java/org/apache/logging/log4j/plugin/processor/PluginProcessor.java
+++ b/log4j-plugin-processor/src/main/java/org/apache/logging/log4j/plugin/processor/PluginProcessor.java
@@@ -1,276 -1,0 +1,273 @@@
 +/*
 + * 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.plugin.processor;
 +
 +import org.apache.logging.log4j.LoggingException;
 +import org.apache.logging.log4j.plugins.Plugin;
 +import org.apache.logging.log4j.plugins.PluginAliases;
 +import org.apache.logging.log4j.plugins.processor.PluginEntry;
- import org.apache.logging.log4j.plugins.util.PluginManager;
 +import org.apache.logging.log4j.util.Strings;
 +
 +import javax.annotation.processing.AbstractProcessor;
 +import javax.annotation.processing.Messager;
 +import javax.annotation.processing.RoundEnvironment;
 +import javax.annotation.processing.SupportedAnnotationTypes;
 +import javax.lang.model.SourceVersion;
 +import javax.lang.model.element.Element;
 +import javax.lang.model.element.ElementVisitor;
 +import javax.lang.model.element.Name;
 +import javax.lang.model.element.TypeElement;
 +import javax.lang.model.util.Elements;
- import javax.lang.model.util.SimpleElementVisitor7;
++import javax.lang.model.util.SimpleElementVisitor8;
 +import javax.tools.Diagnostic.Kind;
 +import javax.tools.FileObject;
 +import javax.tools.JavaFileObject;
 +import javax.tools.StandardLocation;
 +import java.io.BufferedWriter;
 +import java.io.IOException;
 +import java.io.OutputStreamWriter;
 +import java.io.PrintWriter;
 +import java.util.ArrayList;
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.List;
 +import java.util.Locale;
 +import java.util.Map;
 +import java.util.Objects;
 +import java.util.Set;
 +
 +import static java.nio.charset.StandardCharsets.UTF_8;
 +
 +/**
 + * Annotation processor for pre-scanning Log4j 2 plugins.
 + */
 +@SupportedAnnotationTypes({"org.apache.logging.log4j.plugins.*", "org.apache.logging.log4j.core.config.plugins.*"})
 +public class PluginProcessor extends AbstractProcessor {
 +
 +    // TODO: this could be made more abstract to allow for compile-time and run-time plugin processing
 +
 +    private static final String SERVICE_FILE_NAME =
 +            "META-INF/services/org.apache.logging.log4j.plugins.processor.PluginService";
 +
 +    public PluginProcessor() {
 +    }
 +
 +    @Override
 +    public SourceVersion getSupportedSourceVersion() {
 +        return SourceVersion.latest();
 +    }
 +
 +    @Override
 +    public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
 +        Map<String, String> options = processingEnv.getOptions();
 +        String packageName = options.get("pluginPackage");
 +        Messager messager = processingEnv.getMessager();
 +        messager.printMessage(Kind.NOTE, "Processing Log4j annotations");
 +        try {
 +            final Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Plugin.class);
 +            if (elements.isEmpty()) {
 +                messager.printMessage(Kind.NOTE, "No elements to process");
 +                return false;
 +            }
 +            messager.printMessage(Kind.NOTE, "Retrieved " + elements.size() + " Plugin elements");
 +            List<PluginEntry> list = new ArrayList<>();
 +            packageName = collectPlugins(packageName, elements, list);
 +            writeClassFile(packageName, list);
 +            writeServiceFile(packageName);
 +            messager.printMessage(Kind.NOTE, "Annotations processed");
-             return true;
 +        } catch (final Exception ex) {
-             ex.printStackTrace();
 +            error(ex.getMessage());
-             return false;
 +        }
++        return false;
 +    }
 +
 +    private void error(final CharSequence message) {
 +        processingEnv.getMessager().printMessage(Kind.ERROR, message);
 +    }
 +
 +    private String collectPlugins(String packageName, final Iterable<? extends Element> elements, List<PluginEntry> list) {
 +        boolean calculatePackage = packageName == null;
 +        final Elements elementUtils = processingEnv.getElementUtils();
 +        final ElementVisitor<PluginEntry, Plugin> pluginVisitor = new PluginElementVisitor(elementUtils);
 +        final ElementVisitor<Collection<PluginEntry>, Plugin> pluginAliasesVisitor = new PluginAliasesElementVisitor(
 +                elementUtils);
 +        for (final Element element : elements) {
 +            final Plugin plugin = element.getAnnotation(Plugin.class);
 +            if (plugin == null) {
 +                continue;
 +            }
 +            final PluginEntry entry = element.accept(pluginVisitor, plugin);
 +            list.add(entry);
 +            if (calculatePackage) {
 +                packageName = calculatePackage(elementUtils, element, packageName);
 +            }
 +            final Collection<PluginEntry> entries = element.accept(pluginAliasesVisitor, plugin);
 +            for (final PluginEntry pluginEntry : entries) {
 +                list.add(pluginEntry);
 +            }
 +        }
 +        return packageName;
 +    }
 +
 +    private String calculatePackage(Elements elements, Element element, String packageName) {
 +        Name name = elements.getPackageOf(element).getQualifiedName();
 +        if (name == null) {
 +            return null;
 +        }
 +        String pkgName = name.toString();
 +        if (packageName == null) {
 +            return pkgName;
 +        }
 +        if (pkgName.length() == packageName.length()) {
 +            return packageName;
 +        }
 +        if (pkgName.length() < packageName.length() && packageName.startsWith(pkgName)) {
 +            return pkgName;
 +        }
 +
 +        return commonPrefix(pkgName, packageName);
 +    }
 +
 +    private void writeServiceFile(String pkgName) throws IOException {
 +        final FileObject fileObject = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, Strings.EMPTY,
 +                SERVICE_FILE_NAME);
 +        try (final PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(fileObject.openOutputStream(), UTF_8)))) {
 +            writer.println(createFqcn(pkgName));
 +        }
 +    }
 +
 +    private void writeClassFile(String pkg, List<PluginEntry> list) {
 +        String fqcn = createFqcn(pkg);
 +        try (final PrintWriter writer = createSourceFile(fqcn)) {
 +            writer.println("package " + pkg + ".plugins;");
 +            writer.println("");
 +            writer.println("import org.apache.logging.log4j.plugins.processor.PluginEntry;");
 +            writer.println("import org.apache.logging.log4j.plugins.processor.PluginService;");
 +            writer.println("");
 +            writer.println("public class Log4jPlugins extends PluginService {");
 +            writer.println("");
 +            writer.println("    private static PluginEntry[] entries = new PluginEntry[] {");
 +            StringBuilder sb = new StringBuilder();
 +            int max = list.size() - 1;
 +            for (int i = 0; i < list.size(); ++i) {
 +                PluginEntry entry = list.get(i);
 +                sb.append("        ").append("new PluginEntry(\"");
 +                sb.append(entry.getKey()).append("\", \"");
 +                sb.append(entry.getClassName()).append("\", \"");
 +                sb.append(entry.getName()).append("\", ");
 +                sb.append(entry.isPrintable()).append(", ");
 +                sb.append(entry.isDefer()).append(", \"");
 +                sb.append(entry.getCategory()).append("\")");
 +                if (i < max) {
 +                    sb.append(",");
 +                }
 +                writer.println(sb.toString());
 +                sb.setLength(0);
 +            }
 +            writer.println("    };");
 +            writer.println("    @Override");
 +            writer.println("    public PluginEntry[] getEntries() { return entries;}");
 +            writer.println("}");
 +        }
 +    }
 +
 +    private PrintWriter createSourceFile(String fqcn) {
 +        try {
 +            JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(fqcn);
 +            return new PrintWriter(sourceFile.openWriter());
 +        } catch (IOException e) {
 +            throw new LoggingException("Unable to create Plugin Service Class " + fqcn, e);
 +        }
 +    }
 +
 +    private String createFqcn(String packageName) {
 +        return packageName + ".plugins.Log4jPlugins";
 +    }
 +
 +    /**
 +     * ElementVisitor to scan the Plugin annotation.
 +     */
-     private static class PluginElementVisitor extends SimpleElementVisitor7<PluginEntry, Plugin> {
++    private static class PluginElementVisitor extends SimpleElementVisitor8<PluginEntry, Plugin> {
 +
 +        private final Elements elements;
 +
 +        private PluginElementVisitor(final Elements elements) {
 +            this.elements = elements;
 +        }
 +
 +        @Override
 +        public PluginEntry visitType(final TypeElement e, final Plugin plugin) {
 +            Objects.requireNonNull(plugin, "Plugin annotation is null.");
 +            final PluginEntry entry = new PluginEntry();
 +            entry.setKey(plugin.name().toLowerCase(Locale.US));
 +            entry.setClassName(elements.getBinaryName(e).toString());
 +            entry.setName(Plugin.EMPTY.equals(plugin.elementType()) ? plugin.name() : plugin.elementType());
 +            entry.setPrintable(plugin.printObject());
 +            entry.setDefer(plugin.deferChildren());
 +            entry.setCategory(plugin.category());
 +            return entry;
 +        }
 +    }
 +
 +    private String commonPrefix(String str1, String str2) {
-         int minLength = str1.length() < str2.length() ? str1.length() : str2.length();
++        int minLength = Math.min(str1.length(), str2.length());
 +        for (int i = 0; i < minLength; i++) {
 +            if (str1.charAt(i) != str2.charAt(i)) {
 +                if (i > 1 && str1.charAt(i-1) == '.') {
 +                    return str1.substring(0, i-1);
 +                } else {
 +                    return str1.substring(0, i);
 +                }
 +            }
 +        }
 +        return str1.substring(0, minLength);
 +    }
 +
 +    /**
 +     * ElementVisitor to scan the PluginAliases annotation.
 +     */
-     private static class PluginAliasesElementVisitor extends SimpleElementVisitor7<Collection<PluginEntry>, Plugin> {
++    private static class PluginAliasesElementVisitor extends SimpleElementVisitor8<Collection<PluginEntry>, Plugin> {
 +
 +        private final Elements elements;
 +
 +        private PluginAliasesElementVisitor(final Elements elements) {
-             super(Collections.<PluginEntry> emptyList());
++            super(Collections.emptyList());
 +            this.elements = elements;
 +        }
 +
 +        @Override
 +        public Collection<PluginEntry> visitType(final TypeElement e, final Plugin plugin) {
 +            final PluginAliases aliases = e.getAnnotation(PluginAliases.class);
 +            if (aliases == null) {
 +                return DEFAULT_VALUE;
 +            }
 +            final Collection<PluginEntry> entries = new ArrayList<>(aliases.value().length);
 +            for (final String alias : aliases.value()) {
 +                final PluginEntry entry = new PluginEntry();
 +                entry.setKey(alias.toLowerCase(Locale.US));
 +                entry.setClassName(elements.getBinaryName(e).toString());
 +                entry.setName(Plugin.EMPTY.equals(plugin.elementType()) ? alias : plugin.elementType());
 +                entry.setPrintable(plugin.printObject());
 +                entry.setDefer(plugin.deferChildren());
 +                entry.setCategory(plugin.category());
 +                entries.add(entry);
 +            }
 +            return entries;
 +        }
 +    }
 +}
diff --cc log4j-plugin-processor/src/main/java9/module-info.java
index dae6806,0000000..49dbeba
mode 100644,000000..100644
--- a/log4j-plugin-processor/src/main/java9/module-info.java
+++ b/log4j-plugin-processor/src/main/java9/module-info.java
@@@ -1,26 -1,0 +1,28 @@@
 +/*
 + * 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.
 + */
 +module org.apache.logging.log4j.plugin.processor {
 +    exports org.apache.logging.log4j.plugin.processor;
 +
 +    requires java.compiler;
 +    requires org.apache.logging.log4j;
 +    requires org.apache.logging.log4j.plugins;
 +    requires transitive org.osgi.framework;
 +
-     provides javax.annotation.processing.Processor with org.apache.logging.log4j.plugin.processor.PluginProcessor;
++    provides javax.annotation.processing.Processor with
++            org.apache.logging.log4j.plugin.processor.PluginProcessor,
++            org.apache.logging.log4j.plugin.processor.BeanProcessor;
 +}
diff --cc log4j-plugin-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
index e1c421c,0000000..1c97759
mode 100644,000000..100644
--- a/log4j-plugin-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
+++ b/log4j-plugin-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@@ -1,17 -1,0 +1,18 @@@
 +#
 +# Licensed to the Apache Software Foundation (ASF) under one or more
 +# contributor license agreements. See the NOTICE file distributed with
 +# this work for additional information regarding copyright ownership.
 +# The ASF licenses this file to You under the Apache license, Version 2.0
 +# (the "License"); you may not use this file except in compliance with
 +# the License. You may obtain a copy of the License at
 +#
 +#      http://www.apache.org/licenses/LICENSE-2.0
 +#
 +# Unless required by applicable law or agreed to in writing, software
 +# distributed under the License is distributed on an "AS IS" BASIS,
 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 +# See the license for the specific language governing permissions and
 +# limitations under the license.
 +#
 +org.apache.logging.log4j.plugin.processor.PluginProcessor
++org.apache.logging.log4j.plugin.processor.BeanProcessor
diff --cc log4j-plugins-test/pom.xml
index c21c306,0000000..6c32550
mode 100644,000000..100644
--- a/log4j-plugins-test/pom.xml
+++ b/log4j-plugins-test/pom.xml
@@@ -1,312 -1,0 +1,320 @@@
 +<?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
 +  ~ 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.
 +  -->
 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 +  <modelVersion>4.0.0</modelVersion>
 +  <parent>
 +    <groupId>org.apache.logging.log4j</groupId>
 +    <artifactId>log4j</artifactId>
 +    <version>3.0.0-SNAPSHOT</version>
 +    <relativePath>../</relativePath>
 +  </parent>
 +  <artifactId>log4j-plugins-test</artifactId>
 +  <packaging>jar</packaging>
 +  <name>Apache Log4j Plugins Test</name>
 +  <description>Log4j Plugin Test Support</description>
 +  <properties>
 +    <log4jParentDir>${basedir}/..</log4jParentDir>
 +    <docLabel>Plugin Documentation</docLabel>
 +    <projectDir>/plugins</projectDir>
 +    <maven.doap.skip>true</maven.doap.skip>
 +  </properties>
 +  <dependencies>
 +    <dependency>
 +      <groupId>org.apache.logging.log4j</groupId>
 +      <artifactId>log4j-api</artifactId>
 +    </dependency>
 +    <dependency>
 +      <groupId>org.apache.logging.log4j</groupId>
 +      <artifactId>log4j-plugin-processor</artifactId>
 +    </dependency>
 +    <dependency>
 +      <groupId>org.apache.logging.log4j</groupId>
 +      <artifactId>log4j-plugins</artifactId>
 +    </dependency>
 +    <!-- Used for OSGi bundle support -->
 +    <dependency>
 +      <groupId>org.osgi</groupId>
 +      <artifactId>org.osgi.framework</artifactId>
 +      <scope>provided</scope>
 +    </dependency>
 +    <dependency>
 +      <groupId>org.osgi</groupId>
 +      <artifactId>org.osgi.resource</artifactId>
 +      <scope>provided</scope>
 +    </dependency>
 +
 +    <!-- TEST DEPENDENCIES -->
 +
 +    <!-- Pull in useful test classes from API -->
 +    <dependency>
 +      <groupId>org.apache.logging.log4j</groupId>
 +      <artifactId>log4j-api-test</artifactId>
 +      <scope>test</scope>
 +    </dependency>
 +    <dependency>
 +      <groupId>org.junit.vintage</groupId>
 +      <artifactId>junit-vintage-engine</artifactId>
 +      <scope>test</scope>
 +    </dependency>
 +    <dependency>
 +      <groupId>org.junit.jupiter</groupId>
 +      <artifactId>junit-jupiter-engine</artifactId>
 +      <scope>test</scope>
 +    </dependency>
 +    <dependency>
 +      <groupId>org.hamcrest</groupId>
 +      <artifactId>hamcrest</artifactId>
 +      <scope>test</scope>
 +    </dependency>
 +  </dependencies>
 +  <build>
 +    <plugins>
 +      <plugin>
 +        <groupId>org.apache.maven.plugins</groupId>
 +        <artifactId>maven-compiler-plugin</artifactId>
 +        <version>${compiler.plugin.version}</version>
 +        <configuration>
 +          <source>${maven.compiler.source}</source>
 +          <target>${maven.compiler.target}</target>
 +          <release>${maven.compiler.release}</release>
 +          <showDeprecation>true</showDeprecation>
 +          <showWarnings>true</showWarnings>
 +          <verbose>false</verbose>
 +          <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>org.apache.logging.log4j</groupId>
 +              <artifactId>log4j-plugin-processor</artifactId>
 +              <version>${project.version}</version>
 +            </path>
 +            <path>
 +              <groupId>com.google.errorprone</groupId>
 +              <artifactId>error_prone_core</artifactId>
 +              <version>${errorprone.version}</version>
 +            </path>
 +          </annotationProcessorPaths>
 +          <forceJavacCompilerUse>true</forceJavacCompilerUse>
 +          <parameters>true</parameters>
 +        </configuration>
 +        <executions>
 +          <execution>
 +            <id>compile-module-info</id>
 +            <goals>
 +              <goal>compile</goal>
 +            </goals>
 +            <phase>prepare-package</phase>
 +            <configuration>
 +              <compileSourceRoots>
 +                <compileSourceRoot>${project.basedir}/src/main/java9</compileSourceRoot>
 +              </compileSourceRoots>
 +            </configuration>
 +          </execution>
++          <execution>
++            <id>default-testCompile</id>
++            <configuration>
++              <compilerArgs>
++                <arg>-ApluginPackage=org.apache.logging.log4j.plugins.convert.test</arg>
++              </compilerArgs>
++            </configuration>
++          </execution>
 +        </executions>
 +      </plugin>
 +      <plugin>
 +        <artifactId>maven-surefire-plugin</artifactId>
 +        <configuration>
 +          <useModulePath>false</useModulePath>
 +          <excludedGroups>
 +            org.apache.logging.log4j.categories.PerformanceTests
 +          </excludedGroups>
 +          <systemPropertyVariables>
 +            <org.apache.activemq.SERIALIZABLE_PACKAGES>*</org.apache.activemq.SERIALIZABLE_PACKAGES>
 +          </systemPropertyVariables>
 +          <argLine>--add-opens java.base/java.net=ALL-UNNAMED</argLine>
 +        </configuration>
 +      </plugin>
 +      <plugin>
 +        <groupId>org.apache.maven.plugins</groupId>
 +        <artifactId>maven-failsafe-plugin</artifactId>
 +        <configuration>
 +          <skipTests>true</skipTests>
 +        </configuration>
 +      </plugin>
 +      <plugin>
 +        <groupId>org.apache.felix</groupId>
 +        <artifactId>maven-bundle-plugin</artifactId>
 +        <configuration>
 +          <instructions>
 +            <Bundle-SymbolicName>org.apache.logging.log4j.plugins</Bundle-SymbolicName>
 +            <!-- TODO: exclude internal classes from export -->
 +            <Export-Package>org.apache.logging.log4j.plugins.*</Export-Package>
 +            <Import-Package>
 +              org.apache.logging.log4j,
 +              org.apache.logging.log4j.status,
 +              org.apache.logging.log4j.util,
 +              org.osgi.framework.*
 +            </Import-Package>
 +            <Bundle-Activator>org.apache.logging.log4j.plugins.osgi.Activator</Bundle-Activator>
 +          </instructions>
 +        </configuration>
 +      </plugin>
 +    </plugins>
 +  </build>
 +  <reporting>
 +    <plugins>
 +      <plugin>
 +        <groupId>org.apache.maven.plugins</groupId>
 +        <artifactId>maven-changes-plugin</artifactId>
 +        <version>${changes.plugin.version}</version>
 +        <reportSets>
 +          <reportSet>
 +            <reports>
 +              <report>changes-report</report>
 +            </reports>
 +          </reportSet>
 +        </reportSets>
 +        <configuration>
 +          <issueLinkTemplate>%URL%/%ISSUE%</issueLinkTemplate>
 +          <useJql>true</useJql>
 +          <component>Plugins</component>
 +        </configuration>
 +      </plugin>
 +      <plugin>
 +        <groupId>org.apache.maven.plugins</groupId>
 +        <artifactId>maven-checkstyle-plugin</artifactId>
 +        <version>${checkstyle.plugin.version}</version>
 +        <configuration>
 +          <!--<propertiesLocation>${vfs.parent.dir}/checkstyle.properties</propertiesLocation> -->
 +          <configLocation>${log4jParentDir}/checkstyle.xml</configLocation>
 +          <suppressionsLocation>${log4jParentDir}/checkstyle-suppressions.xml</suppressionsLocation>
 +          <enableRulesSummary>false</enableRulesSummary>
 +          <propertyExpansion>basedir=${basedir}</propertyExpansion>
 +          <propertyExpansion>licensedir=${log4jParentDir}/checkstyle-header.txt</propertyExpansion>
 +        </configuration>
 +      </plugin>
 +      <plugin>
 +        <groupId>org.apache.maven.plugins</groupId>
 +        <artifactId>maven-javadoc-plugin</artifactId>
 +        <version>${javadoc.plugin.version}</version>
 +        <configuration>
 +          <failOnError>false</failOnError>
 +          <source>8</source>
 +          <bottom><![CDATA[<p align="center">Copyright &#169; {inceptionYear}-{currentYear} {organizationName}. All Rights Reserved.<br />
 +            Apache Logging, Apache Log4j, Log4j, Apache, the Apache feather logo, the Apache Logging project logo,
 +            and the Apache Log4j logo are trademarks of The Apache Software Foundation.</p>]]></bottom>
 +          <!-- module link generation is completely broken in the javadoc plugin for a multi-module non-aggregating
 +               project -->
 +          <additionalparam>${javadoc.opts}</additionalparam>
 +          <detectOfflineLinks>false</detectOfflineLinks>
 +          <linksource>true</linksource>
 +          <links>
 +            <link>http://docs.oracle.com/javaee/6/api/</link>
 +            <link>http://www.osgi.org/javadoc/r4v43/core/</link>
 +            <link>https://commons.apache.org/proper/commons-lang/javadocs/api-release/</link>
 +          </links>
 +          <groups>
 +            <group>
 +              <title>Core API</title>
 +              <packages>org.apache.logging.log4j.core</packages>
 +            </group>
 +            <group>
 +              <title>Configuration</title>
 +              <packages>org.apache.logging.log4j.core.config*:org.apache.logging.log4j.core.selector</packages>
 +            </group>
 +            <group>
 +              <title>Core Plugins</title>
 +              <packages>org.apache.logging.log4j.core.appender*:org.apache.logging.log4j.core.filter:org.apache.logging.log4j.core.layout:org.apache.logging.log4j.core.lookup:org.apache.logging.log4j.core.pattern:org.apache.logging.log4j.core.script</packages>
 +            </group>
 +            <group>
 +              <title>Tools</title>
 +              <packages>org.apache.logging.log4j.core.net*:org.apache.logging.log4j.core.tools</packages>
 +            </group>
 +            <group>
 +              <title>Internals</title>
 +              <packages>org.apache.logging.log4j.core.async:org.apache.logging.log4j.core.impl:org.apache.logging.log4j.core.util*:org.apache.logging.log4j.core.osgi:org.apache.logging.log4j.core.jackson:org.apache.logging.log4j.core.jmx</packages>
 +            </group>
 +          </groups>
 +        </configuration>
 +        <reportSets>
 +          <reportSet>
 +            <id>non-aggregate</id>
 +            <reports>
 +              <report>javadoc</report>
 +            </reports>
 +          </reportSet>
 +        </reportSets>
 +      </plugin>
 +      <plugin>
 +        <groupId>com.github.spotbugs</groupId>
 +        <artifactId>spotbugs-maven-plugin</artifactId>
 +      </plugin>
 +      <plugin>
 +        <groupId>org.apache.maven.plugins</groupId>
 +        <artifactId>maven-jxr-plugin</artifactId>
 +        <version>${jxr.plugin.version}</version>
 +        <reportSets>
 +          <reportSet>
 +            <id>non-aggregate</id>
 +            <reports>
 +              <report>jxr</report>
 +            </reports>
 +          </reportSet>
 +          <reportSet>
 +            <id>aggregate</id>
 +            <reports>
 +              <report>aggregate</report>
 +            </reports>
 +          </reportSet>
 +        </reportSets>
 +      </plugin>
 +      <plugin>
 +        <groupId>org.apache.maven.plugins</groupId>
 +        <artifactId>maven-pmd-plugin</artifactId>
 +        <version>${pmd.plugin.version}</version>
 +        <configuration>
 +          <targetJdk>${maven.compiler.target}</targetJdk>
 +        </configuration>
 +      </plugin>
 +    </plugins>
 +  </reporting>
 +</project>
 +
diff --cc log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/FakePlugin.java
index 48ea7dc,0000000..d257e51
mode 100644,000000..100644
--- a/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/FakePlugin.java
+++ b/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/FakePlugin.java
@@@ -1,33 -1,0 +1,33 @@@
 +/*
 + * 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.processor;
++package org.apache.logging.log4j.plugins.test.validation;
 +
 +import org.apache.logging.log4j.plugins.Plugin;
 +import org.apache.logging.log4j.plugins.PluginAliases;
 +
 +/**
 + * Test plugin class for unit tests.
 + */
 +@Plugin(name = "Fake", category = "Test")
 +@PluginAliases({"AnotherFake", "StillFake"})
 +public class FakePlugin {
 +
 +    @Plugin(name = "Nested", category = "Test")
 +    public static class Nested {
 +    }
 +}
diff --cc log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/ProductionBean.java
index 0000000,51cd722..51cd722
mode 000000,100644..100644
--- a/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/ProductionBean.java
+++ b/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/ProductionBean.java
diff --cc log4j-plugins-test/src/main/java9/module-info.java
index 6884125,0000000..885afe6
mode 100644,000000..100644
--- a/log4j-plugins-test/src/main/java9/module-info.java
+++ b/log4j-plugins-test/src/main/java9/module-info.java
@@@ -1,24 -1,0 +1,25 @@@
 +/*
 + * 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.
 + */
 +module org.apache.logging.log4j.plugins.test {
 +    exports org.apache.logging.log4j.plugins.test.validation;
 +
 +    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.di.model.PluginModule with org.apache.logging.log4j.plugins.test.validation.plugins.Log4jModule;
 +}
diff --cc log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugin/processor/BeanProcessorTest.java
index 0000000,0000000..624c803
new file mode 100644
--- /dev/null
+++ b/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugin/processor/BeanProcessorTest.java
@@@ -1,0 -1,0 +1,51 @@@
++/*
++ * 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.plugin.processor;
++
++import org.apache.logging.log4j.plugins.di.model.DisposesMethod;
++import org.apache.logging.log4j.plugins.di.model.GenericPlugin;
++import org.apache.logging.log4j.plugins.di.model.InjectionTarget;
++import org.apache.logging.log4j.plugins.di.model.PluginModule;
++import org.apache.logging.log4j.plugins.di.model.ProducerField;
++import org.apache.logging.log4j.plugins.test.validation.FakePlugin;
++import org.apache.logging.log4j.plugins.test.validation.plugins.Log4jModule;
++import org.junit.jupiter.api.Test;
++
++import static org.junit.jupiter.api.Assertions.assertTrue;
++
++class BeanProcessorTest {
++    @Test
++    void smokeTests() {
++        final PluginModule module = new Log4jModule();
++        final var plugins = module.getPluginSources();
++        assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof InjectionTarget &&
++                plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ExampleBean")));
++        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())));
++    }
++}
diff --cc log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugin/processor/PluginProcessorTest.java
index 7a0a7fe,0000000..8d8f02e
mode 100644,000000..100644
--- a/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugin/processor/PluginProcessorTest.java
+++ b/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugin/processor/PluginProcessorTest.java
@@@ -1,112 -1,0 +1,111 @@@
 +/*
 + * 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.plugin.processor;
 +
 +import org.apache.logging.log4j.plugins.Plugin;
 +import org.apache.logging.log4j.plugins.PluginAliases;
- import org.apache.logging.log4j.plugins.processor.FakePlugin;
 +import org.apache.logging.log4j.plugins.processor.PluginEntry;
 +import org.apache.logging.log4j.plugins.processor.PluginService;
++import org.apache.logging.log4j.plugins.test.validation.FakePlugin;
++import org.apache.logging.log4j.plugins.test.validation.plugins.Log4jPlugins;
 +import org.apache.logging.log4j.plugins.util.PluginType;
 +import org.junit.BeforeClass;
 +import org.junit.Test;
 +import org.junit.runner.RunWith;
 +import org.junit.runners.JUnit4;
 +
 +import java.util.List;
 +
 +import static org.junit.Assert.*;
 +
 +@RunWith(JUnit4.class)
 +public class PluginProcessorTest {
 +
 +    private static PluginService pluginService;
 +
 +    private final Plugin p = FakePlugin.class.getAnnotation(Plugin.class);
 +
 +    @BeforeClass
-     public static void setUpClass() throws Exception {
-         Class<?> clazz = PluginProcessor.class.getClassLoader().loadClass("org.apache.logging.log4j.plugins.plugins.Log4jPlugins");
-         assertNotNull("Could not locate plugins class", clazz);
-         pluginService = (PluginService) clazz.getDeclaredConstructor().newInstance();;
++    public static void setUpClass() {
++        pluginService = new Log4jPlugins();
 +    }
 +
 +    @Test
 +    public void testTestCategoryFound() throws Exception {
 +        assertNotNull("No plugin annotation on FakePlugin.", p);
 +        final List<PluginType<?>> testCategory = pluginService.getCategory(p.category());
 +        assertNotEquals("No plugins were found.", 0, pluginService.size());
 +        assertNotNull("The category '" + p.category() + "' was not found.", testCategory);
 +        assertFalse(testCategory.isEmpty());
 +    }
 +
 +    @Test
 +    public void testFakePluginFoundWithCorrectInformation() throws Exception {
 +        final List<PluginType<?>> list = pluginService.getCategory(p.category());
 +        assertNotNull(list);
 +        final PluginEntry fake = getEntry(list, p.name());
 +        assertNotNull(fake);
 +        verifyFakePluginEntry(p.name(), fake);
 +    }
 +
 +    @Test
 +    public void testFakePluginAliasesContainSameInformation() throws Exception {
 +        final PluginAliases aliases = FakePlugin.class.getAnnotation(PluginAliases.class);
 +        for (final String alias : aliases.value()) {
 +            final List<PluginType<?>> list = pluginService.getCategory(p.category());
 +            assertNotNull(list);
 +            final PluginEntry fake = getEntry(list, alias);
 +            assertNotNull(fake);
 +            verifyFakePluginEntry(alias, fake);
 +        }
 +    }
 +
 +    private void verifyFakePluginEntry(final String name, final PluginEntry fake) {
 +        assertNotNull("The plugin '" + name.toLowerCase() + "' was not found.", fake);
 +        assertEquals(FakePlugin.class.getName(), fake.getClassName());
 +        assertEquals(name.toLowerCase(), fake.getKey());
 +        assertEquals(Plugin.EMPTY, p.elementType());
 +        assertEquals(name, fake.getName());
 +        assertEquals(p.printObject(), fake.isPrintable());
 +        assertEquals(p.deferChildren(), fake.isDefer());
 +    }
 +
 +    @Test
 +    public void testNestedPlugin() throws Exception {
 +        final Plugin p = FakePlugin.Nested.class.getAnnotation(Plugin.class);
 +        final List<PluginType<?>> list = pluginService.getCategory(p.category());
 +        assertNotNull(list);
 +        final PluginEntry nested = getEntry(list, p.name());
 +        assertNotNull(nested);
 +        assertEquals(p.name().toLowerCase(), nested.getKey());
 +        assertEquals(FakePlugin.Nested.class.getName(), nested.getClassName());
 +        assertEquals(p.name(), nested.getName());
 +        assertEquals(Plugin.EMPTY, p.elementType());
 +        assertEquals(p.printObject(), nested.isPrintable());
 +        assertEquals(p.deferChildren(), nested.isDefer());
 +    }
 +
 +    private PluginEntry getEntry(List<PluginType<?>> list, String name) {
 +        for (PluginType<?> type : list) {
 +            if (type.getPluginEntry().getName().equalsIgnoreCase(name)) {
 +                return type.getPluginEntry();
 +            }
 +        }
 +        return null;
 +    }
 +}
diff --cc log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/convert/TypeConverterRegistryTest.java
index 008c7bd,0000000..bb37ba8
mode 100644,000000..100644
--- a/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/convert/TypeConverterRegistryTest.java
+++ b/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/convert/TypeConverterRegistryTest.java
@@@ -1,160 -1,0 +1,162 @@@
 +/*
 + * 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.convert;
 +
 +import org.apache.logging.log4j.plugins.Plugin;
- import org.junit.Test;
++import org.junit.jupiter.api.Test;
 +
++import static org.hamcrest.MatcherAssert.*;
 +import static org.hamcrest.Matchers.instanceOf;
- import static org.junit.Assert.*;
++import static org.junit.jupiter.api.Assertions.*;
 +
 +public class TypeConverterRegistryTest {
 +
-     @Test(expected = NullPointerException.class)
++    @Test
 +    public void testFindNullConverter() {
-         TypeConverterRegistry.getInstance().findCompatibleConverter(null);
++        assertThrows(NullPointerException.class,
++                () -> TypeConverterRegistry.getInstance().findCompatibleConverter(null));
 +    }
 +
 +    @Test
 +    public void testFindBooleanConverter() throws Exception {
 +        final TypeConverter<?> converter = TypeConverterRegistry.getInstance().findCompatibleConverter(Boolean.class);
 +        assertNotNull(converter);
 +        assertTrue((Boolean) converter.convert("TRUE"));
 +    }
 +
 +    @Test
 +    public void testFindPrimitiveBooleanConverter() throws Exception {
 +        final TypeConverter<?> converter = TypeConverterRegistry.getInstance().findCompatibleConverter(Boolean.TYPE);
 +        assertNotNull(converter);
 +        assertTrue((Boolean) converter.convert("tRUe"));
 +    }
 +
 +    @SuppressWarnings("unchecked")
 +    @Test
 +    public void testFindCharSequenceConverterUsingStringConverter() throws Exception {
 +        final TypeConverter<CharSequence> converter = (TypeConverter<CharSequence>)
 +            TypeConverterRegistry.getInstance().findCompatibleConverter(CharSequence.class);
 +        assertNotNull(converter);
 +        assertThat(converter, instanceOf(TypeConverters.StringConverter.class));
 +        final CharSequence expected = "This is a test sequence of characters";
 +        final CharSequence actual = converter.convert(expected.toString());
 +        assertEquals(expected, actual);
 +    }
 +
 +    @SuppressWarnings("unchecked")
 +    @Test
 +    public void testFindNumberConverter() throws Exception {
 +        final TypeConverter<Number> numberTypeConverter = (TypeConverter<Number>)
 +            TypeConverterRegistry.getInstance().findCompatibleConverter(Number.class);
 +        assertNotNull(numberTypeConverter);
 +        // TODO: is there a specific converter this should return?
 +    }
 +
 +    public enum Foo {
 +        I, PITY, THE
 +    }
 +
 +    @SuppressWarnings("unchecked")
 +    @Test
 +    public void testFindEnumConverter() throws Exception {
 +        final TypeConverter<Foo> fooTypeConverter = (TypeConverter<Foo>)
 +            TypeConverterRegistry.getInstance().findCompatibleConverter(Foo.class);
 +        assertNotNull(fooTypeConverter);
 +        assertEquals(Foo.I, fooTypeConverter.convert("i"));
 +        assertEquals(Foo.PITY, fooTypeConverter.convert("pity"));
 +        assertEquals(Foo.THE, fooTypeConverter.convert("THE"));
 +    }
 +
 +    public static final class CustomTestClass1 {
 +
 +        private CustomTestClass1() {}
 +
 +    }
 +
 +    @Plugin(name = "CustomTestClass1Converter1", category = TypeConverters.CATEGORY)
 +    public static final class CustomTestClass1Converter1
 +            implements TypeConverter<CustomTestClass1> {
 +
 +        @Override
 +        public CustomTestClass1 convert(final String ignored) {
 +            return new CustomTestClass1();
 +        }
 +
 +    }
 +
 +    @SuppressWarnings("ComparableType")
 +    @Plugin(name = "CustomTestClass1Converter2", category = TypeConverters.CATEGORY)
 +    public static final class CustomTestClass1Converter2
 +            implements TypeConverter<CustomTestClass1>, Comparable<TypeConverter<?>> {
 +
 +        @Override
 +        public CustomTestClass1 convert(final String ignored) {
 +            return new CustomTestClass1();
 +        }
 +
 +        @Override
 +        public int compareTo(@SuppressWarnings("NullableProblems") final TypeConverter<?> converter) {
 +            return -1;
 +        }
 +
 +    }
 +
 +    @Test
 +    public void testMultipleComparableConverters() {
 +        final TypeConverter<?> converter = TypeConverterRegistry
 +                .getInstance()
 +                .findCompatibleConverter(CustomTestClass1.class);
 +        assertThat(converter, instanceOf(CustomTestClass1Converter2.class));
 +    }
 +
 +    public static final class CustomTestClass2 {
 +
 +        private CustomTestClass2() {}
 +
 +    }
 +
 +    @Plugin(name = "CustomTestClass2Converter1", category = TypeConverters.CATEGORY)
 +    public static final class CustomTestClass2Converter1
 +            implements TypeConverter<CustomTestClass2> {
 +
 +        @Override
 +        public CustomTestClass2 convert(final String ignored) {
 +            return new CustomTestClass2();
 +        }
 +
 +    }
 +
 +    @Plugin(name = "CustomTestClass2Converter2", category = TypeConverters.CATEGORY)
 +    public static final class CustomTestClass2Converter2
 +            implements TypeConverter<CustomTestClass2> {
 +
 +        @Override
 +        public CustomTestClass2 convert(final String ignored) {
 +            return new CustomTestClass2();
 +        }
 +
 +    }
 +
 +    @Test
 +    public void testMultipleIncomparableConverters() {
 +        final TypeConverter<?> converter = TypeConverterRegistry
 +                .getInstance()
 +                .findCompatibleConverter(CustomTestClass2.class);
 +        assertThat(converter, instanceOf(CustomTestClass2Converter1.class));
 +    }
 +
 +}
diff --cc log4j-plugins/src/main/java/module-info.java
index 37820a4,0000000..20f1f17
mode 100644,000000..100644
--- a/log4j-plugins/src/main/java/module-info.java
+++ b/log4j-plugins/src/main/java/module-info.java
@@@ -1,34 -1,0 +1,36 @@@
 +/*
 + * 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.
 + */
 +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.model;
 +    exports org.apache.logging.log4j.plugins.processor;
 +    exports org.apache.logging.log4j.plugins.util;
 +    exports org.apache.logging.log4j.plugins.validation;
 +    exports org.apache.logging.log4j.plugins.validation.constraints;
 +    exports org.apache.logging.log4j.plugins.validation.validators;
 +    exports org.apache.logging.log4j.plugins.bind;
 +    exports org.apache.logging.log4j.plugins.inject;
 +    exports org.apache.logging.log4j.plugins.name;
 +
-     requires java.compiler;
 +    requires org.apache.logging.log4j;
 +    requires transitive org.osgi.framework;
 +
 +    uses org.apache.logging.log4j.plugins.processor.PluginService;
++    provides org.apache.logging.log4j.plugins.processor.PluginService with org.apache.logging.log4j.plugins.convert.plugins.Log4jPlugins;
 +}