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 <[email protected]> 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; +}
