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:13 UTC
[logging-log4j2] 01/01: Merge branch 'mean-bean-machine'
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 © {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;
+}