This is an automated email from the ASF dual-hosted git repository. mattsicker pushed a commit to branch mean-bean-machine in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 7ff8c9cd05413ed4ac54bd4f2e79481aa29070f4 Author: Matt Sicker <[email protected]> AuthorDate: Sat Jul 10 19:54:19 2021 -0500 Add foundation for bean annotation processing and plugin metadata --- log4j-core/pom.xml | 1 + .../logging/log4j/core/config/di/BeanManager.java | 70 +++-- .../core/config/di/impl/DefaultBeanManager.java | 10 +- .../config/di/impl/DefaultInjectionTarget.java | 6 +- .../di/impl/DefaultInjectionTargetFactory.java | 10 +- .../log4j/core/config/di/impl/Injector.java | 3 +- .../log4j/core/config/plugins/PluginAttribute.java | 2 + .../config/plugins/PluginBuilderAttribute.java | 2 + .../core/config/plugins/PluginBuilderFactory.java | 9 +- .../core/config/plugins/PluginConfiguration.java | 2 + .../log4j/core/config/plugins/PluginElement.java | 2 + .../log4j/core/config/plugins/PluginFactory.java | 9 +- .../log4j/core/config/plugins/PluginNode.java | 2 + .../log4j/core/config/plugins/PluginValue.java | 2 + log4j-plugins/pom.xml | 14 +- .../org/apache/logging/log4j/plugins/Plugin.java | 3 + .../logging/log4j/plugins/PluginAliases.java | 2 + .../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 + .../org/apache/logging/log4j/plugins/di/Named.java | 1 + .../logging/log4j/plugins/di/NamedAliases.java | 1 + .../di/{AnnotationAlias.java => Producer.java} | 12 +- .../apache/logging/log4j/plugins/di/Produces.java | 3 +- .../plugins/di/{Named.java => Qualifier.java} | 13 +- .../log4j/plugins/di/spi/BeanInfoService.java | 52 ++++ .../log4j/plugins/name/NamedAliasesProvider.java | 2 +- .../log4j/plugins/name/PluginNameProvider.java | 20 +- .../log4j/plugins/processor/BeanProcessor.java | 342 +++++++++++++++++++++ .../log4j/plugins/processor/PluginProcessor.java | 14 +- .../log4j/plugins/processor/PluginService.java | 4 +- .../logging/log4j/plugins/util/AnnotationUtil.java | 10 +- log4j-plugins/src/main/java9/module-info.java | 9 +- .../services/javax.annotation.processing.Processor | 18 ++ .../plugins/test/validation/ExampleBean.java} | 38 ++- .../plugins/test/validation/ImplicitBean.java} | 37 ++- .../test/validation/ImplicitMethodBean.java} | 45 ++- .../plugins/test/validation/ProductionBean.java | 56 ++++ .../log4j/plugins/processor/BeanProcessorTest.java | 54 ++++ log4j-plugins/src/test/java9/module-info.java | 20 +- 43 files changed, 759 insertions(+), 154 deletions(-) diff --git a/log4j-core/pom.xml b/log4j-core/pom.xml index 0d5c9c9..8402405 100644 --- a/log4j-core/pom.xml +++ b/log4j-core/pom.xml @@ -330,6 +330,7 @@ <includes> <include>module-info.class</include> <include>**/Log4jPlugins.class</include> + <include>**/Log4jBeanInfo.class</include> </includes> </fileset> <fileset> diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/BeanManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/BeanManager.java index 4e35ae6..3168e1b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/BeanManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/BeanManager.java @@ -18,9 +18,9 @@ package org.apache.logging.log4j.core.config.di; import org.apache.logging.log4j.plugins.di.Inject; -import org.apache.logging.log4j.plugins.di.Produces; +import org.apache.logging.log4j.plugins.di.Producer; +import org.apache.logging.log4j.plugins.di.Qualifier; import org.apache.logging.log4j.plugins.name.AnnotatedElementNameProvider; -import org.apache.logging.log4j.plugins.name.NameProvider; import org.apache.logging.log4j.plugins.util.AnnotationUtil; import org.apache.logging.log4j.util.Strings; @@ -106,7 +106,7 @@ public interface BeanManager extends AutoCloseable { * Checks if a class has exactly one injectable constructor. A constructor is <i>injectable</i> if: * <ol> * <li>it is annotated with {@link Inject}; or</li> - * <li>it has as least one parameter annotated with {@link Inject} or a {@linkplain NameProvider name provider annotation}; or</li> + * <li>it has as least one parameter annotated with a {@linkplain Qualifier qualifier annotation}; or</li> * <li>it is the lone no-arg constructor.</li> * </ol> * @@ -114,60 +114,60 @@ public interface BeanManager extends AutoCloseable { * @return true if the class has exactly one injectable constructor or false otherwise */ default boolean isInjectable(final Class<?> type) { + boolean result = false; int injectConstructors = 0; final Constructor<?>[] constructors = type.getDeclaredConstructors(); for (final Constructor<?> constructor : constructors) { - if (AnnotationUtil.isAnnotationPresent(constructor, Inject.class)) { + if (constructor.isAnnotationPresent(Inject.class)) { injectConstructors++; } } - if (injectConstructors > 1) { - return false; - } - if (injectConstructors == 1) { - return true; - } - - int implicitConstructors = 0; - for (final Constructor<?> constructor : constructors) { - for (final Parameter parameter : constructor.getParameters()) { - if (isInjectable(parameter)) { - implicitConstructors++; - break; + if (injectConstructors <= 1) { + if (injectConstructors == 1) { + result = true; + } else { + int implicitConstructors = 0; + for (final Constructor<?> constructor : constructors) { + for (final Parameter parameter : constructor.getParameters()) { + if (AnnotatedElementNameProvider.hasName(parameter)) { + implicitConstructors++; + break; + } + } + } + if (implicitConstructors <= 1) { + if (implicitConstructors == 1) { + result = true; + } else { + try { + type.getDeclaredConstructor(); + result = true; + } catch (final NoSuchMethodException ignored) { + } + } } } } - if (implicitConstructors > 1) { - return false; - } - if (implicitConstructors == 1) { - return true; - } - try { - type.getDeclaredConstructor(); - return true; - } catch (final NoSuchMethodException ignored) { - return false; - } + return result; } /** * Checks if an element is injectable. An element is <i>injectable</i> if: * <ol> * <li>it is annotated with {@link Inject}; or</li> - * <li>it is annotated with a {@linkplain NameProvider name provider annotation} and is not annotated - * with {@link Produces}.</li> + * <li>it is annotated with a {@linkplain Qualifier qualifier annotation} + * and is not annotated with a {@link Producer} annotation.</li> * </ol> * * @param element field, method, or parameter to check * @return true if the element is injectable or false otherwise */ default boolean isInjectable(final AnnotatedElement element) { - if (AnnotationUtil.isAnnotationPresent(element, Inject.class)) { + if (element.isAnnotationPresent(Inject.class)) { return true; } - if (AnnotationUtil.isAnnotationPresent(element, Produces.class)) { + if (AnnotationUtil.isMetaAnnotationPresent(element, Producer.class)) { return false; } return AnnotatedElementNameProvider.hasName(element); @@ -258,4 +258,8 @@ public interface BeanManager extends AutoCloseable { // TODO: integrate with TypeConverters // TODO: need some sort of default value strategy to bridge over @PluginAttribute and optional injected values // TODO: add support for injecting collections and arrays + // TODO: begin integrating with singleton beans in log4j-core + // TODO: LoggerContext scope (sort of like a singleton/application scope with each LoggerContext) + // TODO: configuration scope? should be similar to LoggerContext scope but can be restarted/reconfigured at runtime + // TODO: update annotation processor to output bean descriptors for lazy loading (attempt to provide partial type closures?) } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java index a835ae0..7dc6921 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java @@ -32,7 +32,7 @@ import org.apache.logging.log4j.core.config.di.UnsatisfiedBeanException; import org.apache.logging.log4j.core.config.di.ValidationException; import org.apache.logging.log4j.plugins.di.DependentScoped; import org.apache.logging.log4j.plugins.di.Disposes; -import org.apache.logging.log4j.plugins.di.Produces; +import org.apache.logging.log4j.plugins.di.Producer; import org.apache.logging.log4j.plugins.di.Provider; import org.apache.logging.log4j.plugins.di.ScopeType; import org.apache.logging.log4j.plugins.di.SingletonScoped; @@ -88,7 +88,7 @@ public class DefaultBeanManager implements BeanManager { loadDisposerMethods(beanClass, bean); for (Class<?> clazz = beanClass; clazz != null; clazz = clazz.getSuperclass()) { for (final Method method : clazz.getDeclaredMethods()) { - if (AnnotationUtil.isAnnotationPresent(method, Produces.class)) { + if (AnnotationUtil.isMetaAnnotationPresent(method, Producer.class)) { method.setAccessible(true); loadedBeans.add(createBean(method, bean)); } @@ -96,7 +96,7 @@ public class DefaultBeanManager implements BeanManager { } for (Class<?> clazz = beanClass; clazz != null; clazz = clazz.getSuperclass()) { for (final Field field : clazz.getDeclaredFields()) { - if (AnnotationUtil.isAnnotationPresent(field, Produces.class)) { + if (AnnotationUtil.isMetaAnnotationPresent(field, Producer.class)) { field.setAccessible(true); loadedBeans.add(createBean(field, bean)); } @@ -173,7 +173,7 @@ public class DefaultBeanManager implements BeanManager { private void loadDisposerMethods(final Class<?> beanClass, final Bean<?> bean) { for (final Method method : beanClass.getDeclaredMethods()) { for (final Parameter parameter : method.getParameters()) { - if (AnnotationUtil.isAnnotationPresent(parameter, Disposes.class)) { + if (parameter.isAnnotationPresent(Disposes.class)) { final String name = AnnotatedElementNameProvider.getName(parameter); final Collection<String> aliases = AnnotatedElementAliasesProvider.getAliases(parameter); method.setAccessible(true); @@ -217,7 +217,7 @@ public class DefaultBeanManager implements BeanManager { @Override public void validateInjectionPoint(final InjectionPoint point) { final AnnotatedElement element = point.getElement(); - if (AnnotationUtil.isAnnotationPresent(element, Produces.class)) { + if (AnnotationUtil.isMetaAnnotationPresent(element, Producer.class)) { throw new DefinitionException("Cannot inject into a @Produces element: " + element); } final Type type = point.getType(); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTarget.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTarget.java index a6ebed9..46eaad9 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTarget.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTarget.java @@ -23,7 +23,7 @@ import org.apache.logging.log4j.core.config.di.InjectionPoint; import org.apache.logging.log4j.core.config.di.InjectionTarget; import org.apache.logging.log4j.plugins.di.Disposes; import org.apache.logging.log4j.plugins.di.Inject; -import org.apache.logging.log4j.plugins.di.Produces; +import org.apache.logging.log4j.plugins.di.Producer; import org.apache.logging.log4j.plugins.util.AnnotationUtil; import org.apache.logging.log4j.plugins.util.TypeUtil; @@ -86,8 +86,8 @@ class DefaultInjectionTarget<T> implements InjectionTarget<T> { final Member member = point.getMember(); final AnnotatedElement element = point.getElement(); if (member instanceof Method && !injectedMethods.contains(member) && - !AnnotationUtil.isAnnotationPresent(element, Produces.class) && - !AnnotationUtil.isAnnotationPresent(element, Disposes.class)) { + !AnnotationUtil.isMetaAnnotationPresent(element, Producer.class) && + !element.isAnnotationPresent(Disposes.class)) { final Method method = TypeUtil.cast(member); final Set<InjectionPoint> methodInjectionPoints = injectionPoints.stream() .filter(p -> method.equals(p.getMember())) diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTargetFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTargetFactory.java index 4d99fd7..d3ed010 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTargetFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTargetFactory.java @@ -26,7 +26,7 @@ import org.apache.logging.log4j.core.config.di.InjectionTargetFactory; import org.apache.logging.log4j.plugins.di.Inject; import org.apache.logging.log4j.plugins.di.PostConstruct; import org.apache.logging.log4j.plugins.di.PreDestroy; -import org.apache.logging.log4j.plugins.util.AnnotationUtil; +import org.apache.logging.log4j.plugins.name.AnnotatedElementNameProvider; import org.apache.logging.log4j.plugins.util.TypeUtil; import java.lang.reflect.AccessibleObject; @@ -67,7 +67,7 @@ class DefaultInjectionTargetFactory<T> implements InjectionTargetFactory<T> { private Constructor<T> getInjectableConstructor() { final Constructor<?>[] allConstructors = type.getDeclaredConstructors(); final List<Constructor<?>> injectConstructors = Arrays.stream(allConstructors) - .filter(constructor -> AnnotationUtil.isAnnotationPresent(constructor, Inject.class)) + .filter(constructor -> constructor.isAnnotationPresent(Inject.class)) .collect(Collectors.toList()); if (injectConstructors.size() > 1) { throw new DefinitionException("Found more than one constructor with @Inject for " + type); @@ -78,7 +78,7 @@ class DefaultInjectionTargetFactory<T> implements InjectionTargetFactory<T> { return TypeUtil.cast(constructor); } final List<Constructor<?>> injectParameterConstructors = Arrays.stream(allConstructors) - .filter(constructor -> Arrays.stream(constructor.getParameters()).anyMatch(beanManager::isInjectable)) + .filter(constructor -> Arrays.stream(constructor.getParameters()).anyMatch(AnnotatedElementNameProvider::hasName)) .collect(Collectors.toList()); if (injectParameterConstructors.size() > 1) { throw new DefinitionException("No @Inject constructors found and remaining constructors ambiguous for " + type); @@ -134,7 +134,7 @@ class DefaultInjectionTargetFactory<T> implements InjectionTargetFactory<T> { final List<Method> postConstructMethods = new ArrayList<>(); for (Class<?> clazz = type; clazz != null; clazz = clazz.getSuperclass()) { for (final Method method : clazz.getDeclaredMethods()) { - if (AnnotationUtil.isAnnotationPresent(method, PostConstruct.class)) { + if (method.isAnnotationPresent(PostConstruct.class)) { postConstructMethods.add(0, method); } } @@ -148,7 +148,7 @@ class DefaultInjectionTargetFactory<T> implements InjectionTargetFactory<T> { final List<Method> preDestroyMethods = new ArrayList<>(); for (Class<?> clazz = type; clazz != null; clazz = clazz.getSuperclass()) { for (final Method method : clazz.getDeclaredMethods()) { - if (AnnotationUtil.isAnnotationPresent(method, PreDestroy.class)) { + if (method.isAnnotationPresent(PreDestroy.class)) { preDestroyMethods.add(method); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/Injector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/Injector.java index e29b6ad..eff10f4 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/Injector.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/Injector.java @@ -22,7 +22,6 @@ import org.apache.logging.log4j.core.config.di.InitializationContext; import org.apache.logging.log4j.core.config.di.InitializationException; import org.apache.logging.log4j.core.config.di.InjectionPoint; import org.apache.logging.log4j.plugins.di.Disposes; -import org.apache.logging.log4j.plugins.util.AnnotationUtil; import org.apache.logging.log4j.plugins.util.TypeUtil; import java.lang.reflect.Constructor; @@ -96,7 +95,7 @@ public class Injector { final Object[] arguments = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { final Parameter parameter = parameters[i]; - if (AnnotationUtil.isAnnotationPresent(parameter, Disposes.class)) { + if (parameter.isAnnotationPresent(Disposes.class)) { arguments[i] = producedInstance; } else { final InjectionPoint injectionPoint = injectionPoints.stream() diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAttribute.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAttribute.java index a4d62d3..843564f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAttribute.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAttribute.java @@ -18,6 +18,7 @@ package org.apache.logging.log4j.core.config.plugins; import org.apache.logging.log4j.core.config.plugins.util.PluginAttributeNameProvider; import org.apache.logging.log4j.core.config.plugins.visitors.PluginAttributeVisitor; +import org.apache.logging.log4j.plugins.di.Qualifier; import org.apache.logging.log4j.plugins.inject.InjectorStrategy; import org.apache.logging.log4j.plugins.name.NameProvider; import org.apache.logging.log4j.util.Strings; @@ -41,6 +42,7 @@ import java.lang.annotation.Target; @Target({ElementType.PARAMETER, ElementType.FIELD}) @InjectorStrategy(PluginAttributeVisitor.class) @NameProvider(PluginAttributeNameProvider.class) +@Qualifier public @interface PluginAttribute { /** diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderAttribute.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderAttribute.java index a7b4a8b..97b4f1b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderAttribute.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderAttribute.java @@ -19,6 +19,7 @@ package org.apache.logging.log4j.core.config.plugins; import org.apache.logging.log4j.core.config.plugins.util.PluginBuilderAttributeNameProvider; import org.apache.logging.log4j.core.config.plugins.visitors.PluginBuilderAttributeVisitor; +import org.apache.logging.log4j.plugins.di.Qualifier; import org.apache.logging.log4j.plugins.inject.InjectorStrategy; import org.apache.logging.log4j.plugins.name.NameProvider; import org.apache.logging.log4j.util.Strings; @@ -38,6 +39,7 @@ import java.lang.annotation.Target; @Target({ElementType.PARAMETER, ElementType.FIELD}) @InjectorStrategy(PluginBuilderAttributeVisitor.class) @NameProvider(PluginBuilderAttributeNameProvider.class) +@Qualifier public @interface PluginBuilderAttribute { /** diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java index 0ea7221..11b2955 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java @@ -17,7 +17,13 @@ package org.apache.logging.log4j.core.config.plugins; -import java.lang.annotation.*; +import org.apache.logging.log4j.plugins.di.Produces; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * Marks a method as a factory for custom Plugin builders. @@ -26,6 +32,7 @@ import java.lang.annotation.*; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) +@Produces public @interface PluginBuilderFactory { // empty } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginConfiguration.java index dee6f67..e9bc4c5 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginConfiguration.java @@ -17,6 +17,7 @@ package org.apache.logging.log4j.core.config.plugins; import org.apache.logging.log4j.core.config.plugins.inject.PluginConfigurationInjector; +import org.apache.logging.log4j.plugins.di.Qualifier; import org.apache.logging.log4j.plugins.inject.InjectorStrategy; import java.lang.annotation.Documented; @@ -34,6 +35,7 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD}) @InjectorStrategy(PluginConfigurationInjector.class) +@Qualifier public @interface PluginConfiguration { // empty } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginElement.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginElement.java index b832da9..70b2d9e 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginElement.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginElement.java @@ -18,6 +18,7 @@ package org.apache.logging.log4j.core.config.plugins; import org.apache.logging.log4j.core.config.plugins.util.PluginElementNameProvider; import org.apache.logging.log4j.core.config.plugins.visitors.PluginElementVisitor; +import org.apache.logging.log4j.plugins.di.Qualifier; import org.apache.logging.log4j.plugins.inject.InjectorStrategy; import org.apache.logging.log4j.plugins.name.NameProvider; @@ -36,6 +37,7 @@ import java.lang.annotation.Target; @Target({ElementType.PARAMETER, ElementType.FIELD}) @InjectorStrategy(PluginElementVisitor.class) @NameProvider(PluginElementNameProvider.class) +@Qualifier public @interface PluginElement { /** diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginFactory.java index 2e25631..abe1cf4 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginFactory.java @@ -16,7 +16,13 @@ */ package org.apache.logging.log4j.core.config.plugins; -import java.lang.annotation.*; +import org.apache.logging.log4j.plugins.di.Produces; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * Identifies a Method as the factory to create the plugin. This annotation should only be used on a {@code static} @@ -29,6 +35,7 @@ import java.lang.annotation.*; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) +@Produces public @interface PluginFactory { // empty } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginNode.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginNode.java index 14a136c..7765a4b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginNode.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginNode.java @@ -17,6 +17,7 @@ package org.apache.logging.log4j.core.config.plugins; import org.apache.logging.log4j.core.config.plugins.visitors.PluginNodeVisitor; +import org.apache.logging.log4j.plugins.di.Qualifier; import org.apache.logging.log4j.plugins.inject.InjectorStrategy; import java.lang.annotation.Documented; @@ -33,6 +34,7 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.FIELD}) @InjectorStrategy(PluginNodeVisitor.class) +@Qualifier public @interface PluginNode { // empty } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginValue.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginValue.java index bcbadc0..0e925d0 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginValue.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginValue.java @@ -18,6 +18,7 @@ package org.apache.logging.log4j.core.config.plugins; import org.apache.logging.log4j.core.config.plugins.util.PluginValueNameProvider; import org.apache.logging.log4j.core.config.plugins.visitors.PluginValueVisitor; +import org.apache.logging.log4j.plugins.di.Qualifier; import org.apache.logging.log4j.plugins.inject.InjectorStrategy; import org.apache.logging.log4j.plugins.name.NameProvider; @@ -39,6 +40,7 @@ import java.lang.annotation.Target; @Target({ElementType.PARAMETER, ElementType.FIELD}) @InjectorStrategy(PluginValueVisitor.class) @NameProvider(PluginValueNameProvider.class) +@Qualifier public @interface PluginValue { String value(); diff --git a/log4j-plugins/pom.xml b/log4j-plugins/pom.xml index a78d381..c562089 100644 --- a/log4j-plugins/pom.xml +++ b/log4j-plugins/pom.xml @@ -105,6 +105,7 @@ <includes> <include>module-info.class</include> <include>**/Log4jPlugins.class</include> + <include>**/Log4jBeanInfo.class</include> </includes> </fileset> <fileset> @@ -142,9 +143,11 @@ <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> <proc>only</proc> - <compilerArguments> - <processor>org.apache.logging.log4j.plugins.processor.PluginProcessor</processor> - </compilerArguments> + <annotationProcessors> + <annotationProcessor>org.apache.logging.log4j.plugins.processor.PluginProcessor</annotationProcessor> + <annotationProcessor>org.apache.logging.log4j.plugins.processor.BeanProcessor</annotationProcessor> + </annotationProcessors> + <parameters>true</parameters> </configuration> </execution> <execution> @@ -160,6 +163,7 @@ <proc>only</proc> <annotationProcessors> <annotationProcessor>org.apache.logging.log4j.plugins.processor.PluginProcessor</annotationProcessor> + <annotationProcessor>org.apache.logging.log4j.plugins.processor.BeanProcessor</annotationProcessor> </annotationProcessors> <compileSourceRoots> <compileSourceRoot>${project.basedir}/src/test/java-test</compileSourceRoot> @@ -180,7 +184,11 @@ <proc>only</proc> <annotationProcessors> <annotationProcessor>org.apache.logging.log4j.plugins.processor.PluginProcessor</annotationProcessor> + <annotationProcessor>org.apache.logging.log4j.plugins.processor.BeanProcessor</annotationProcessor> </annotationProcessors> + <compilerArguments> + <ApluginPackage>org.apache.logging.log4j.plugins</ApluginPackage> + </compilerArguments> <compileSourceRoots> <compileSourceRoot>${project.basedir}/src/test/java</compileSourceRoot> <compileSourceRoot>${project.basedir}/src/test/java-test</compileSourceRoot> diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Plugin.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Plugin.java index 18f04ee..9ef058c 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Plugin.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Plugin.java @@ -16,6 +16,8 @@ */ package org.apache.logging.log4j.plugins; +import org.apache.logging.log4j.plugins.name.NameProvider; +import org.apache.logging.log4j.plugins.name.PluginNameProvider; import org.apache.logging.log4j.util.Strings; import java.lang.annotation.Documented; @@ -30,6 +32,7 @@ import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) +@NameProvider(PluginNameProvider.class) public @interface Plugin { /** diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAliases.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAliases.java index 07acea7..e3f41c8 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAliases.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAliases.java @@ -16,6 +16,7 @@ */ package org.apache.logging.log4j.plugins; +import org.apache.logging.log4j.plugins.di.Qualifier; import org.apache.logging.log4j.plugins.name.AliasesProvider; import org.apache.logging.log4j.plugins.name.PluginAliasesProvider; @@ -33,6 +34,7 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.TYPE, ElementType.FIELD, ElementType.METHOD}) @AliasesProvider(PluginAliasesProvider.class) +@Qualifier public @interface PluginAliases { /** diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAttribute.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAttribute.java index d844d02..4b5c9f0 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAttribute.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAttribute.java @@ -16,6 +16,7 @@ */ package org.apache.logging.log4j.plugins; +import org.apache.logging.log4j.plugins.di.Qualifier; import org.apache.logging.log4j.plugins.inject.InjectorStrategy; import org.apache.logging.log4j.plugins.inject.PluginAttributeInjector; import org.apache.logging.log4j.plugins.name.NameProvider; @@ -47,6 +48,7 @@ import java.lang.annotation.Target; @Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD}) @InjectorStrategy(PluginAttributeInjector.class) @NameProvider(PluginAttributeNameProvider.class) +@Qualifier public @interface PluginAttribute { /** diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginBuilderAttribute.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginBuilderAttribute.java index a0a39b5..2eebabe 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginBuilderAttribute.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginBuilderAttribute.java @@ -17,6 +17,7 @@ package org.apache.logging.log4j.plugins; +import org.apache.logging.log4j.plugins.di.Qualifier; import org.apache.logging.log4j.plugins.inject.InjectorStrategy; import org.apache.logging.log4j.plugins.inject.PluginBuilderAttributeInjector; import org.apache.logging.log4j.plugins.name.NameProvider; @@ -39,6 +40,7 @@ import java.lang.annotation.Target; @Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.TYPE}) @InjectorStrategy(PluginBuilderAttributeInjector.class) @NameProvider(PluginBuilderAttributeNameProvider.class) +@Qualifier @Deprecated public @interface PluginBuilderAttribute { diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginElement.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginElement.java index fa296ca..6951de1 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginElement.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginElement.java @@ -16,6 +16,7 @@ */ package org.apache.logging.log4j.plugins; +import org.apache.logging.log4j.plugins.di.Qualifier; import org.apache.logging.log4j.plugins.inject.InjectorStrategy; import org.apache.logging.log4j.plugins.inject.PluginElementInjector; import org.apache.logging.log4j.plugins.name.NameProvider; @@ -38,6 +39,7 @@ import java.lang.annotation.Target; @Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD}) @InjectorStrategy(PluginElementInjector.class) @NameProvider(PluginElementNameProvider.class) +@Qualifier public @interface PluginElement { /** diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginFactory.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginFactory.java index f1d89c6..f32bc38 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginFactory.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginFactory.java @@ -16,6 +16,8 @@ */ package org.apache.logging.log4j.plugins; +import org.apache.logging.log4j.plugins.di.Producer; + import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -37,6 +39,7 @@ import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) +@Producer public @interface PluginFactory { // empty } diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginNode.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginNode.java index dcc5b0c..eec9d8f 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginNode.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginNode.java @@ -16,6 +16,7 @@ */ package org.apache.logging.log4j.plugins; +import org.apache.logging.log4j.plugins.di.Qualifier; import org.apache.logging.log4j.plugins.inject.InjectorStrategy; import org.apache.logging.log4j.plugins.inject.PluginNodeInjector; @@ -34,6 +35,7 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD}) @InjectorStrategy(PluginNodeInjector.class) +@Qualifier public @interface PluginNode { // empty } diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginValue.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginValue.java index 5e7a3fe..b320306 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginValue.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginValue.java @@ -16,6 +16,7 @@ */ package org.apache.logging.log4j.plugins; +import org.apache.logging.log4j.plugins.di.Qualifier; import org.apache.logging.log4j.plugins.inject.InjectorStrategy; import org.apache.logging.log4j.plugins.inject.PluginValueInjector; import org.apache.logging.log4j.plugins.name.NameProvider; @@ -42,6 +43,7 @@ import java.lang.annotation.Target; @Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD}) @InjectorStrategy(PluginValueInjector.class) @NameProvider(PluginValueNameProvider.class) +@Qualifier public @interface PluginValue { /** diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java index 0c7a542..b8b48d4 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java @@ -30,6 +30,7 @@ import java.lang.annotation.RetentionPolicy; @Documented @NameProvider(NamedQualifierNameProvider.class) @Repeatable(NamedAliases.class) +@Qualifier public @interface Named { String value() default Strings.EMPTY; } diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/NamedAliases.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/NamedAliases.java index 6903d2b..94adf0c 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/NamedAliases.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/NamedAliases.java @@ -32,6 +32,7 @@ import java.lang.annotation.Target; @Documented @NameProvider(NamedAliasesProvider.class) @AliasesProvider(NamedAliasesProvider.class) +@Qualifier public @interface NamedAliases { Named[] value(); } diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/AnnotationAlias.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Producer.java similarity index 70% rename from log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/AnnotationAlias.java rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Producer.java index d570c3b..286723f 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/AnnotationAlias.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Producer.java @@ -17,22 +17,14 @@ package org.apache.logging.log4j.plugins.di; -import java.lang.annotation.Annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -/** - * Meta annotation for making an annotation an alias for another annotation. Annotations with this annotation will be - * interpreted as if they were implemented by the given annotation type instead. This applies to - * {@linkplain ScopeType scopes}, {@link Inject}, {@link Produces}, {@link Disposes}, {@link PostConstruct}, and - * {@link PreDestroy}. - */ -@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) +@Retention(RetentionPolicy.RUNTIME) @Documented -public @interface AnnotationAlias { - Class<? extends Annotation> value(); +public @interface Producer { } diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Produces.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Produces.java index ba641dd..2739193 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Produces.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Produces.java @@ -45,8 +45,9 @@ import java.lang.annotation.Target; * @see <a href="https://docs.jboss.org/cdi/api/2.0/javax/enterprise/inject/Produces.html">CDI @Produces API Docs</a> * @see <a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html">Spring @Bean API Docs</a> */ -@Target({ElementType.METHOD, ElementType.FIELD}) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented +@Producer public @interface Produces { } diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Qualifier.java similarity index 73% copy from log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java copy to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Qualifier.java index 0c7a542..85bbbb0 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Qualifier.java @@ -17,19 +17,14 @@ package org.apache.logging.log4j.plugins.di; -import org.apache.logging.log4j.plugins.name.NameProvider; -import org.apache.logging.log4j.plugins.name.NamedQualifierNameProvider; -import org.apache.logging.log4j.util.Strings; - import java.lang.annotation.Documented; -import java.lang.annotation.Repeatable; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) @Documented -@NameProvider(NamedQualifierNameProvider.class) -@Repeatable(NamedAliases.class) -public @interface Named { - String value() default Strings.EMPTY; +public @interface Qualifier { } diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/spi/BeanInfoService.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/spi/BeanInfoService.java new file mode 100644 index 0000000..f7f45e1 --- /dev/null +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/spi/BeanInfoService.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package org.apache.logging.log4j.plugins.di.spi; + +import java.util.List; +import java.util.Map; + +public abstract class BeanInfoService { + private final List<String> injectableClassNames; + private final List<String> producibleClassNames; + private final List<String> destructibleClassNames; + private final Map<String, List<String>> pluginCategories; + + protected BeanInfoService(final List<String> injectableClassNames, final List<String> producibleClassNames, + final List<String> destructibleClassNames, final Map<String, List<String>> pluginCategories) { + this.injectableClassNames = injectableClassNames; + this.producibleClassNames = producibleClassNames; + this.destructibleClassNames = destructibleClassNames; + this.pluginCategories = pluginCategories; + } + + public List<String> getInjectableClassNames() { + return injectableClassNames; + } + + public List<String> getProducibleClassNames() { + return producibleClassNames; + } + + public List<String> getDestructibleClassNames() { + return destructibleClassNames; + } + + public Map<String, List<String>> getPluginCategories() { + return pluginCategories; + } +} diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/NamedAliasesProvider.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/NamedAliasesProvider.java index a3e12c1..9471784 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/NamedAliasesProvider.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/NamedAliasesProvider.java @@ -49,6 +49,6 @@ public class NamedAliasesProvider implements AnnotatedElementNameProvider<NamedA for (int i = 0; i < size; i++) { aliases.add(named[i + 1].value()); } - return aliases; + return Collections.unmodifiableCollection(aliases); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/PluginNameProvider.java similarity index 67% copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java copy to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/PluginNameProvider.java index 0ea7221..7e71643 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/PluginNameProvider.java @@ -15,17 +15,15 @@ * limitations under the license. */ -package org.apache.logging.log4j.core.config.plugins; +package org.apache.logging.log4j.plugins.name; -import java.lang.annotation.*; +import org.apache.logging.log4j.plugins.Plugin; -/** - * Marks a method as a factory for custom Plugin builders. - * @deprecated Exists for compatibility with Log4j 2 2.x plugins. Not used for Log4j 2 3.x plugins. - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface PluginBuilderFactory { - // empty +import java.util.Optional; + +public class PluginNameProvider implements AnnotatedElementNameProvider<Plugin> { + @Override + public Optional<String> getSpecifiedName(final Plugin annotation) { + return Optional.of(annotation.name()); + } } diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/BeanProcessor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/BeanProcessor.java new file mode 100644 index 0000000..013bab6 --- /dev/null +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/BeanProcessor.java @@ -0,0 +1,342 @@ +/* + * 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; + +import org.apache.logging.log4j.plugins.di.Disposes; +import org.apache.logging.log4j.plugins.di.Inject; +import org.apache.logging.log4j.plugins.di.Producer; +import org.apache.logging.log4j.plugins.di.Qualifier; +import org.apache.logging.log4j.util.Strings; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +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.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.util.ElementKindVisitor9; +import javax.lang.model.util.Elements; +import javax.lang.model.util.SimpleAnnotationValueVisitor9; +import javax.lang.model.util.Types; +import javax.tools.FileObject; +import javax.tools.JavaFileObject; +import javax.tools.StandardLocation; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.UncheckedIOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +// TODO: migrate to separate maven module between log4j-plugins and log4j-core +@SupportedAnnotationTypes({"org.apache.logging.log4j.plugins.*", "org.apache.logging.log4j.core.config.plugins.*"}) +@SupportedOptions("pluginPackage") +public class BeanProcessor extends AbstractProcessor { + public static final String BEAN_INFO_SERVICE_FILE = "META-INF/services/org.apache.logging.log4j.plugins.di.spi.BeanInfoService"; + + public BeanProcessor() { + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + + private static class ProducerAnnotationVisitor extends ElementKindVisitor9<Void, Void> { + private final Set<ExecutableElement> producerMethods = new HashSet<>(); + private final Set<VariableElement> producerFields = new HashSet<>(); + + @Override + public Void visitVariableAsField(final VariableElement e, final Void unused) { + producerFields.add(e); + return null; + } + + @Override + public Void visitExecutableAsMethod(final ExecutableElement e, final Void unused) { + producerMethods.add(e); + return null; + } + } + + private static class DisposesAnnotationVisitor extends ElementKindVisitor9<Void, Void> { + private final Set<ExecutableElement> disposesMethods = new HashSet<>(); + + @Override + public Void visitVariableAsParameter(final VariableElement e, final Void unused) { + disposesMethods.add((ExecutableElement) e.getEnclosingElement()); + return null; + } + } + + private static class InjectAnnotationVisitor extends ElementKindVisitor9<Void, Void> { + private final Set<TypeElement> injectableClasses = new HashSet<>(); + + @Override + public Void visitVariableAsField(final VariableElement e, final Void unused) { + injectableClasses.add((TypeElement) e.getEnclosingElement()); + return null; + } + + @Override + public Void visitExecutableAsConstructor(final ExecutableElement e, final Void unused) { + injectableClasses.add((TypeElement) e.getEnclosingElement()); + return null; + } + + @Override + public Void visitExecutableAsMethod(final ExecutableElement e, final Void unused) { + injectableClasses.add((TypeElement) e.getEnclosingElement()); + return null; + } + } + + private static class QualifiedAnnotationVisitor extends ElementKindVisitor9<Void, Void> { + private final Predicate<AnnotationMirror> isProducerAnnotation; + private final Set<TypeElement> injectableClasses = new HashSet<>(); + + private 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((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(typeElement); + } + return null; + } + } + + private static class PluginAnnotationVisitor extends ElementKindVisitor9<Void, Void> { + private final Map<String, Set<TypeElement>> pluginCategories = new HashMap<>(); + + @Override + public Void visitTypeAsClass(final TypeElement e, final Void unused) { + final AnnotationMirror pluginAnnotation = e.getAnnotationMirrors() + .stream() + .filter(ann -> ann.getAnnotationType().asElement().getSimpleName().contentEquals("Plugin")) + .findAny() + .orElseThrow(); + final ExecutableElement categoryKey = pluginAnnotation.getElementValues() + .keySet() + .stream() + .filter(element -> element.getSimpleName().contentEquals("category")) + .findAny() + .orElseThrow(); + + final String category = pluginAnnotation.getElementValues() + .get(categoryKey) + .accept(new SimpleAnnotationValueVisitor9<String, Void>() { + @Override + public String visitString(final String s, final Void unused1) { + return s; + } + }, null); + pluginCategories.computeIfAbsent(category, ignored -> new HashSet<>()).add(e); + + return null; + } + } + + @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<ExecutableElement> producerMethods = producesAnnotationVisitor.producerMethods; + final Set<VariableElement> producerFields = producesAnnotationVisitor.producerFields; + final Set<ExecutableElement> disposesMethods = disposesAnnotationVisitor.disposesMethods; + final Set<TypeElement> injectableClasses = injectAnnotationVisitor.injectableClasses; + final Set<TypeElement> implicitInjectableClasses = qualifiedAnnotationVisitor.injectableClasses; + final Map<String, Set<TypeElement>> pluginCategories = pluginAnnotationVisitor.pluginCategories; + final Set<PackageElement> packageElements = new HashSet<>(); + + final Elements elements = processingEnv.getElementUtils(); + final Set<CharSequence> producibleClassNames = Stream.concat( + producerMethods.stream() + .map(e -> (TypeElement) e.getEnclosingElement()) + .peek(e -> packageElements.add(elements.getPackageOf(e))) + .map(elements::getBinaryName), + producerFields.stream() + .map(e -> (TypeElement) e.getEnclosingElement()) + .peek(e -> packageElements.add(elements.getPackageOf(e))) + .map(elements::getBinaryName)) + .collect(Collectors.toSet()); + final Set<CharSequence> destructibleClassNames = disposesMethods.stream() + .map(e -> (TypeElement) e.getEnclosingElement()) + .peek(e -> packageElements.add(elements.getPackageOf(e))) + .map(elements::getBinaryName) + .collect(Collectors.toSet()); + final Set<CharSequence> injectableClassNames = Stream.concat( + injectableClasses.stream() + .peek(e -> packageElements.add(elements.getPackageOf(e))) + .map(elements::getBinaryName), + implicitInjectableClasses.stream() + .peek(e -> packageElements.add(elements.getPackageOf(e))) + .map(elements::getBinaryName)) + .collect(Collectors.toSet()); + final Map<String, List<CharSequence>> pluginClassNames = pluginCategories.entrySet().stream().collect( + Collectors.toMap(Map.Entry::getKey, + entry -> entry.getValue().stream() + .peek(el -> packageElements.add(elements.getPackageOf(el))) + .map(elements::getBinaryName) + .sorted(CharSequence::compare) + .collect(Collectors.toList()))); + + String packageName = processingEnv.getOptions().get("pluginPackage"); + if (packageName == null) { + packageName = packageElements.stream() + .map(PackageElement::getQualifiedName) + .map(CharSequence.class::cast) + .reduce(BeanProcessor::commonPrefix) + .orElseThrow() + .toString(); + } + try { + writeBeanInfoServiceFile(packageName); + writeBeanInfoServiceClass(packageName, injectableClassNames, producibleClassNames, destructibleClassNames, pluginClassNames); + return false; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private void writeBeanInfoServiceFile(final String packageName) throws IOException { + final FileObject fileObject = processingEnv.getFiler() + .createResource(StandardLocation.CLASS_OUTPUT, "", BEAN_INFO_SERVICE_FILE); + try (PrintWriter out = new PrintWriter(fileObject.openWriter())) { + out.println(packageName + ".plugins.Log4jBeanInfo"); + } + } + + private void writeBeanInfoServiceClass(final String packageName, final Set<CharSequence> injectableClassNames, + final Set<CharSequence> producibleClassNames, + final Set<CharSequence> destructibleClassNames, + final Map<String, List<CharSequence>> pluginClassNames) + throws IOException { + final JavaFileObject sourceFile = processingEnv.getFiler() + .createSourceFile(packageName + ".plugins.Log4jBeanInfo"); + try (final PrintWriter out = new PrintWriter(sourceFile.openWriter())) { + out.println("package " + packageName + ".plugins;"); + out.println(); + out.println("import java.util.List;"); + out.println("import java.util.Map;"); + out.println(); + out.println("@javax.annotation.processing.Generated(\"" + getClass().getName() + "\")"); + out.println("public class Log4jBeanInfo extends org.apache.logging.log4j.plugins.di.spi.BeanInfoService {"); + out.println(); + out.println(" private static final List<String> INJECTABLE = List.of(" + getListOfNames(injectableClassNames) + ");"); + out.println(); + out.println(" private static final List<String> PRODUCIBLE = List.of(" + getListOfNames(producibleClassNames) + ");"); + out.println(); + out.println(" private static final List<String> DESTRUCTIBLE = List.of(" + getListOfNames(destructibleClassNames) + ");"); + out.println(); + out.println(" private static final Map<String, List<String>> PLUGIN_CATEGORIES = Map.of(" + getMapOfPluginNames(pluginClassNames) + ");"); + out.println(); + out.println(" public Log4jBeanInfo() {"); + out.println(" super(INJECTABLE, PRODUCIBLE, DESTRUCTIBLE, PLUGIN_CATEGORIES);"); + out.println(" }"); + out.println(); + out.println("}"); + } + } + + private static String getListOfNames(final Set<CharSequence> names) { + return names.isEmpty() ? Strings.EMPTY : names.stream().sorted(CharSequence::compare).collect( + Collectors.joining("\",\n \"", "\n \"", "\"\n ")); + } + + private static String getMapOfPluginNames(final Map<String, List<CharSequence>> pluginClassNames) { + return pluginClassNames.isEmpty() ? Strings.EMPTY : pluginClassNames.entrySet() + .stream() + .sorted(Map.Entry.comparingByKey()) + .map(e -> '"' + e.getKey() + "\", List.of(" + e.getValue().stream().collect(Collectors.joining("\",\n \"", "\n \"", "\"\n ")) + ')') + .collect(Collectors.joining(",\n ", "\n ", "\n ")); + } + + private 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 --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginProcessor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginProcessor.java index ec18989..09bef4f 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginProcessor.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginProcessor.java @@ -32,7 +32,7 @@ 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; @@ -95,12 +95,10 @@ public class PluginProcessor extends AbstractProcessor { 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) { @@ -209,7 +207,7 @@ public class PluginProcessor extends AbstractProcessor { /** * 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; @@ -232,7 +230,7 @@ public class PluginProcessor extends AbstractProcessor { } 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) == '.') { @@ -248,12 +246,12 @@ public class PluginProcessor extends AbstractProcessor { /** * 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; } diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginService.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginService.java index 86c068a..f6122dc 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginService.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginService.java @@ -18,9 +18,9 @@ package org.apache.logging.log4j.plugins.processor; import org.apache.logging.log4j.plugins.util.PluginType; +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -35,7 +35,7 @@ public abstract class PluginService { PluginEntry[] entries = getEntries(); for (PluginEntry entry : entries) { String category = entry.getCategory().toLowerCase(); - List<PluginType<?>> list = categories.computeIfAbsent(category, ignored -> new LinkedList<>()); + List<PluginType<?>> list = categories.computeIfAbsent(category, ignored -> new ArrayList<>()); PluginType<?> type = new PluginType<>(entry, this.getClass().getClassLoader()); list.add(type); } diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/AnnotationUtil.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/AnnotationUtil.java index 7bfcc3f..4b5abc4 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/AnnotationUtil.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/AnnotationUtil.java @@ -17,20 +17,14 @@ package org.apache.logging.log4j.plugins.util; -import org.apache.logging.log4j.plugins.di.AnnotationAlias; - import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; public final class AnnotationUtil { - public static boolean isAnnotationPresent(final AnnotatedElement element, final Class<? extends Annotation> annotationType) { - if (element.isAnnotationPresent(annotationType)) { - return true; - } + public static boolean isMetaAnnotationPresent(final AnnotatedElement element, final Class<? extends Annotation> metaAnnotation) { for (final Annotation annotation : element.getAnnotations()) { - final AnnotationAlias alias = annotation.annotationType().getAnnotation(AnnotationAlias.class); - if (alias != null && annotationType.equals(alias.value())) { + if (annotation.annotationType().isAnnotationPresent(metaAnnotation)) { return true; } } diff --git a/log4j-plugins/src/main/java9/module-info.java b/log4j-plugins/src/main/java9/module-info.java index b4144dc..4506235 100644 --- a/log4j-plugins/src/main/java9/module-info.java +++ b/log4j-plugins/src/main/java9/module-info.java @@ -18,6 +18,7 @@ module org.apache.logging.log4j.plugins { exports org.apache.logging.log4j.plugins; exports org.apache.logging.log4j.plugins.convert; exports org.apache.logging.log4j.plugins.di; + exports org.apache.logging.log4j.plugins.di.spi; exports org.apache.logging.log4j.plugins.processor; exports org.apache.logging.log4j.plugins.util; exports org.apache.logging.log4j.plugins.validation; @@ -27,12 +28,14 @@ module org.apache.logging.log4j.plugins { exports org.apache.logging.log4j.plugins.inject; exports org.apache.logging.log4j.plugins.name; - requires java.compiler; - requires org.apache.logging.log4j; + requires transitive java.compiler; // TODO: break out annotation processor into separate module + requires transitive org.apache.logging.log4j; requires transitive org.osgi.framework; provides org.apache.logging.log4j.plugins.processor.PluginService with org.apache.logging.log4j.plugins.convert.plugins.Log4jPlugins; - provides javax.annotation.processing.Processor with org.apache.logging.log4j.plugins.processor.PluginProcessor; + provides org.apache.logging.log4j.plugins.di.spi.BeanInfoService with org.apache.logging.log4j.plugins.convert.plugins.Log4jBeanInfo; + provides javax.annotation.processing.Processor with org.apache.logging.log4j.plugins.processor.PluginProcessor, org.apache.logging.log4j.plugins.processor.BeanProcessor; uses org.apache.logging.log4j.plugins.processor.PluginService; +// uses org.apache.logging.log4j.plugins.di.spi.BeanInfoService; } diff --git a/log4j-plugins/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/log4j-plugins/src/main/resources/META-INF/services/javax.annotation.processing.Processor index 5d6951a..4aebe10 100644 --- a/log4j-plugins/src/main/resources/META-INF/services/javax.annotation.processing.Processor +++ b/log4j-plugins/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -14,4 +14,22 @@ # See the license for the specific language governing permissions and # limitations under the license. # + +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache license, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the license for the specific language governing permissions and +# limitations under the license. +# org.apache.logging.log4j.plugins.processor.PluginProcessor +org.apache.logging.log4j.plugins.processor.BeanProcessor diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java b/log4j-plugins/src/test/java-test/org/apache/logging/log4j/plugins/test/validation/ExampleBean.java similarity index 55% copy from log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java copy to log4j-plugins/src/test/java-test/org/apache/logging/log4j/plugins/test/validation/ExampleBean.java index 0c7a542..5a89264 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java +++ b/log4j-plugins/src/test/java-test/org/apache/logging/log4j/plugins/test/validation/ExampleBean.java @@ -14,22 +14,32 @@ * See the license for the specific language governing permissions and * limitations under the license. */ +package org.apache.logging.log4j.plugins.test.validation; -package org.apache.logging.log4j.plugins.di; +import java.util.Map; +import org.apache.logging.log4j.plugins.di.Inject; -import org.apache.logging.log4j.plugins.name.NameProvider; -import org.apache.logging.log4j.plugins.name.NamedQualifierNameProvider; -import org.apache.logging.log4j.util.Strings; +public class ExampleBean { + private final String alpha; + private final int beta; + private final Map<String, String> gamma; -import java.lang.annotation.Documented; -import java.lang.annotation.Repeatable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; + @Inject + public ExampleBean(final String alpha, final int beta, final Map<String, String> gamma) { + this.alpha = alpha; + this.beta = beta; + this.gamma = gamma; + } -@Retention(RetentionPolicy.RUNTIME) -@Documented -@NameProvider(NamedQualifierNameProvider.class) -@Repeatable(NamedAliases.class) -public @interface Named { - String value() default Strings.EMPTY; + public String getAlpha() { + return alpha; + } + + public int getBeta() { + return beta; + } + + public Map<String, String> getGamma() { + return gamma; + } } diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java b/log4j-plugins/src/test/java-test/org/apache/logging/log4j/plugins/test/validation/ImplicitBean.java similarity index 55% copy from log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java copy to log4j-plugins/src/test/java-test/org/apache/logging/log4j/plugins/test/validation/ImplicitBean.java index 0c7a542..fd75e5f 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java +++ b/log4j-plugins/src/test/java-test/org/apache/logging/log4j/plugins/test/validation/ImplicitBean.java @@ -14,22 +14,31 @@ * See the license for the specific language governing permissions and * limitations under the license. */ +package org.apache.logging.log4j.plugins.test.validation; -package org.apache.logging.log4j.plugins.di; +import java.util.Map; +import org.apache.logging.log4j.plugins.di.Named; -import org.apache.logging.log4j.plugins.name.NameProvider; -import org.apache.logging.log4j.plugins.name.NamedQualifierNameProvider; -import org.apache.logging.log4j.util.Strings; +public class ImplicitBean { + private final String alpha; + private final int beta; + private final Map<String, String> gamma; -import java.lang.annotation.Documented; -import java.lang.annotation.Repeatable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; + public ImplicitBean(@Named final String alpha, final int beta, final Map<String, String> gamma) { + this.alpha = alpha; + this.beta = beta; + this.gamma = gamma; + } -@Retention(RetentionPolicy.RUNTIME) -@Documented -@NameProvider(NamedQualifierNameProvider.class) -@Repeatable(NamedAliases.class) -public @interface Named { - String value() default Strings.EMPTY; + public String getAlpha() { + return alpha; + } + + public int getBeta() { + return beta; + } + + public Map<String, String> getGamma() { + return gamma; + } } diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/AnnotationUtil.java b/log4j-plugins/src/test/java-test/org/apache/logging/log4j/plugins/test/validation/ImplicitMethodBean.java similarity index 50% copy from log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/AnnotationUtil.java copy to log4j-plugins/src/test/java-test/org/apache/logging/log4j/plugins/test/validation/ImplicitMethodBean.java index 7bfcc3f..c36b0ef 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/AnnotationUtil.java +++ b/log4j-plugins/src/test/java-test/org/apache/logging/log4j/plugins/test/validation/ImplicitMethodBean.java @@ -14,29 +14,40 @@ * See the license for the specific language governing permissions and * limitations under the license. */ +package org.apache.logging.log4j.plugins.test.validation; -package org.apache.logging.log4j.plugins.util; +import java.util.Map; +import org.apache.logging.log4j.plugins.di.Named; -import org.apache.logging.log4j.plugins.di.AnnotationAlias; +public class ImplicitMethodBean { + private String alpha; + private int beta; + private Map<String, String> gamma; -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; + public String getAlpha() { + return alpha; + } + + public ImplicitMethodBean setAlpha(@Named final String alpha) { + this.alpha = alpha; + return this; + } -public final class AnnotationUtil { + public int getBeta() { + return beta; + } + + public ImplicitMethodBean setBeta(@Named final int beta) { + this.beta = beta; + return this; + } - public static boolean isAnnotationPresent(final AnnotatedElement element, final Class<? extends Annotation> annotationType) { - if (element.isAnnotationPresent(annotationType)) { - return true; - } - for (final Annotation annotation : element.getAnnotations()) { - final AnnotationAlias alias = annotation.annotationType().getAnnotation(AnnotationAlias.class); - if (alias != null && annotationType.equals(alias.value())) { - return true; - } - } - return false; + public Map<String, String> getGamma() { + return gamma; } - private AnnotationUtil() { + public ImplicitMethodBean setGamma(@Named final Map<String, String> gamma) { + this.gamma = gamma; + return this; } } diff --git a/log4j-plugins/src/test/java-test/org/apache/logging/log4j/plugins/test/validation/ProductionBean.java b/log4j-plugins/src/test/java-test/org/apache/logging/log4j/plugins/test/validation/ProductionBean.java new file mode 100644 index 0000000..51cd722 --- /dev/null +++ b/log4j-plugins/src/test/java-test/org/apache/logging/log4j/plugins/test/validation/ProductionBean.java @@ -0,0 +1,56 @@ +/* + * 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.test.validation; + +import org.apache.logging.log4j.plugins.di.Disposes; +import org.apache.logging.log4j.plugins.di.Inject; +import org.apache.logging.log4j.plugins.di.Produces; +import org.apache.logging.log4j.plugins.di.Provider; + +public class ProductionBean { + private final String alpha; + + private ProductionBean(final String alpha) { + this.alpha = alpha; + } + + public String getAlpha() { + return alpha; + } + + @Produces + public static final String ALPHA = "hello world"; + + public static void destroyBean(@Disposes ProductionBean bean) { + System.out.println(bean.getAlpha()); + } + + public static class Builder implements Provider<ProductionBean> { + private String alpha; + + @Inject + public Builder setAlpha(String alpha) { + this.alpha = alpha; + return this; + } + + @Override + public ProductionBean get() { + return new ProductionBean(alpha); + } + } +} diff --git a/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/BeanProcessorTest.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/BeanProcessorTest.java new file mode 100644 index 0000000..b63070c --- /dev/null +++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/BeanProcessorTest.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package org.apache.logging.log4j.plugins.processor; + +import org.apache.logging.log4j.plugins.di.spi.BeanInfoService; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.ServiceLoader; + +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class BeanProcessorTest { + @Test + void canLoadTestCategory() { + final BeanInfoService service = ServiceLoader.load(BeanInfoService.class, getClass().getClassLoader()) + .findFirst() + .orElseThrow(); + final List<String> testPlugins = service.getPluginCategories().get("Test"); + assertNotNull(testPlugins); + assertNotEquals(0, testPlugins.size()); + assertTrue(testPlugins.stream().anyMatch(name -> name.equals(FakePlugin.class.getName()))); + } + + @Test + void smokeTests() { + final BeanInfoService service = ServiceLoader.load(BeanInfoService.class, getClass().getClassLoader()) + .findFirst() + .orElseThrow(); + assertTrue(service.getInjectableClassNames().stream().anyMatch(name -> name.equals("org.apache.logging.log4j.plugins.test.validation.ExampleBean"))); + assertTrue(service.getInjectableClassNames().stream().anyMatch(name -> name.equals("org.apache.logging.log4j.plugins.test.validation.ImplicitBean"))); + assertTrue(service.getInjectableClassNames().stream().anyMatch(name -> name.equals("org.apache.logging.log4j.plugins.test.validation.ImplicitMethodBean"))); + assertTrue(service.getInjectableClassNames().stream().anyMatch(name -> name.equals("org.apache.logging.log4j.plugins.test.validation.ProductionBean$Builder"))); + assertTrue(service.getProducibleClassNames().stream().anyMatch(name -> name.equals("org.apache.logging.log4j.plugins.test.validation.ProductionBean"))); + assertTrue(service.getDestructibleClassNames().stream().anyMatch(name -> name.equals("org.apache.logging.log4j.plugins.test.validation.ProductionBean"))); + } +} \ No newline at end of file diff --git a/log4j-plugins/src/test/java9/module-info.java b/log4j-plugins/src/test/java9/module-info.java index 3ce37fc..41a87f2 100644 --- a/log4j-plugins/src/test/java9/module-info.java +++ b/log4j-plugins/src/test/java9/module-info.java @@ -1,9 +1,21 @@ open module org.apache.logging.log4j.plugins { exports org.apache.logging.log4j.plugins; + exports org.apache.logging.log4j.plugins.convert; + exports org.apache.logging.log4j.plugins.di; + exports org.apache.logging.log4j.plugins.di.spi; + exports org.apache.logging.log4j.plugins.name; + exports org.apache.logging.log4j.plugins.processor; + exports org.apache.logging.log4j.plugins.util; + exports org.apache.logging.log4j.plugins.bind; + exports org.apache.logging.log4j.plugins.inject; + + 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.test.validation; - requires java.compiler; - requires org.apache.logging.log4j; + requires transitive java.compiler; + requires transitive org.apache.logging.log4j; requires org.apache.logging.log4j.test; requires org.junit.jupiter.api; requires org.junit.jupiter.engine; @@ -12,7 +24,9 @@ open module org.apache.logging.log4j.plugins { requires junit; provides org.apache.logging.log4j.plugins.processor.PluginService with org.apache.logging.log4j.plugins.convert.plugins.Log4jPlugins; - provides javax.annotation.processing.Processor with org.apache.logging.log4j.plugins.processor.PluginProcessor; + provides org.apache.logging.log4j.plugins.di.spi.BeanInfoService with org.apache.logging.log4j.plugins.convert.plugins.Log4jBeanInfo; + provides javax.annotation.processing.Processor with org.apache.logging.log4j.plugins.processor.PluginProcessor, org.apache.logging.log4j.plugins.processor.BeanProcessor; uses org.apache.logging.log4j.plugins.processor.PluginService; + uses org.apache.logging.log4j.plugins.di.spi.BeanInfoService; }
