Skip to content

Commit e6e6ddd

Browse files
committed
Add log4j.plugin.enableBndAnnotations option to PluginProcessor
This adds a `log4j.plugin.enableBndAnnotations` option to the `PluginProcessor`. Its default value is inferred from the compiler classpath. We also rename the `pluginPackage` option to a more coherent `log4j.plugin.package` option. Closes #3251
1 parent 7fd3a13 commit e6e6ddd

File tree

7 files changed

+384
-137
lines changed

7 files changed

+384
-137
lines changed

log4j-parent/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -865,7 +865,7 @@
865865
<id>default-testCompile</id>
866866
<configuration>
867867
<compilerArgs combine.children="append">
868-
<arg>-ApluginPackage=${log4jPluginPackageForTests}</arg>
868+
<arg>-Alog4j.plugin.package=${log4jPluginPackageForTests}</arg>
869869
</compilerArgs>
870870
</configuration>
871871
</execution>

log4j-plugin-processor/pom.xml

+45-4
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@
3131
<name>Apache Log4j Plugin Processor</name>
3232
<description>Log4j Plugin Annotation Processor</description>
3333

34-
<properties>
35-
<log4jParentDir>${basedir}/..</log4jParentDir>
36-
</properties>
37-
3834
<dependencies>
3935

4036
<dependency>
@@ -47,6 +43,51 @@
4743
<artifactId>log4j-plugins</artifactId>
4844
</dependency>
4945

46+
<dependency>
47+
<groupId>org.assertj</groupId>
48+
<artifactId>assertj-core</artifactId>
49+
<scope>test</scope>
50+
</dependency>
51+
52+
<dependency>
53+
<groupId>commons-io</groupId>
54+
<artifactId>commons-io</artifactId>
55+
<scope>test</scope>
56+
</dependency>
57+
58+
<dependency>
59+
<groupId>org.junit.jupiter</groupId>
60+
<artifactId>junit-jupiter-api</artifactId>
61+
<scope>test</scope>
62+
</dependency>
63+
64+
<dependency>
65+
<groupId>org.junit-pioneer</groupId>
66+
<artifactId>junit-pioneer</artifactId>
67+
<scope>test</scope>
68+
</dependency>
69+
5070
</dependencies>
5171

72+
<build>
73+
<plugins>
74+
75+
<plugin>
76+
<groupId>org.apache.maven.plugins</groupId>
77+
<artifactId>maven-surefire-plugin</artifactId>
78+
<executions>
79+
<execution>
80+
<id>test-no-bnd-annotations</id>
81+
<configuration>
82+
<classpathDependencyExcludes>
83+
<exclude>biz.aQute.bnd:biz.aQute.bnd.annotation</exclude>
84+
</classpathDependencyExcludes>
85+
</configuration>
86+
</execution>
87+
</executions>
88+
</plugin>
89+
90+
</plugins>
91+
</build>
92+
5293
</project>

log4j-plugin-processor/src/main/java/org/apache/logging/log4j/plugin/processor/PluginProcessor.java

+72-20
Original file line numberDiff line numberDiff line change
@@ -57,41 +57,71 @@
5757
import org.apache.logging.log4j.plugins.PluginAliases;
5858
import org.apache.logging.log4j.plugins.model.PluginEntry;
5959
import org.apache.logging.log4j.util.Strings;
60+
import org.jspecify.annotations.NullMarked;
6061

6162
/**
62-
* Annotation processor for pre-scanning Log4j plugins. This generates implementation classes extending
63-
* {@link org.apache.logging.log4j.plugins.model.PluginService} with a list of {@link PluginEntry} instances
64-
* discovered from plugin annotations. By default, this will use the most specific package name it can derive
65-
* from where the annotated plugins are located in a subpackage {@code plugins}. The output base package name
66-
* can be overridden via the {@code pluginPackage} annotation processor option.
63+
* Annotation processor to generate a {@link org.apache.logging.log4j.plugins.model.PluginService} implementation.
64+
* <p>
65+
* This generates a {@link org.apache.logging.log4j.plugins.model.PluginService} implementation with a list of
66+
* {@link PluginEntry} instances.
67+
* The fully qualified class name of the generated service is:
68+
* </p>
69+
* <pre>
70+
* {@code <log4j.plugin.package>.plugins.Log4jPlugins}
71+
* </pre>
72+
* <p>
73+
* where {@code <log4j.plugin.package>} is the effective value of the {@link #PLUGIN_PACKAGE} option.
74+
* </p>
6775
*/
76+
@NullMarked
6877
@SupportedAnnotationTypes({"org.apache.logging.log4j.plugins.*", "org.apache.logging.log4j.core.config.plugins.*"})
6978
@ServiceProvider(value = Processor.class, resolution = Resolution.OPTIONAL)
7079
public class PluginProcessor extends AbstractProcessor {
7180

72-
// TODO: this could be made more abstract to allow for compile-time and run-time plugin processing
81+
/**
82+
* Option name to enable or disable the generation of {@link aQute.bnd.annotation.spi.ServiceConsumer} annotations.
83+
* <p>
84+
* The default behavior depends on the presence of {@code biz.aQute.bnd.annotation} on the classpath.
85+
* </p>
86+
*/
87+
public static final String ENABLE_BND_ANNOTATIONS = "log4j.plugin.enableBndAnnotations";
88+
89+
/**
90+
* Option name to determine the package containing the generated {@link org.apache.logging.log4j.plugins.model.PluginService}
91+
* <p>
92+
* If absent, the value of this option is the common prefix of all Log4j Plugin classes.
93+
* </p>
94+
*/
95+
public static final String PLUGIN_PACKAGE = "log4j.plugin.package";
7396

7497
private static final String SERVICE_FILE_NAME =
7598
"META-INF/services/org.apache.logging.log4j.plugins.model.PluginService";
7699

100+
private boolean enableBndAnnotations;
101+
private String packageName = "";
102+
77103
public PluginProcessor() {}
78104

105+
@Override
106+
public Set<String> getSupportedOptions() {
107+
return Set.of(ENABLE_BND_ANNOTATIONS, PLUGIN_PACKAGE);
108+
}
109+
79110
@Override
80111
public SourceVersion getSupportedSourceVersion() {
81112
return SourceVersion.latest();
82113
}
83114

84115
@Override
85116
public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
86-
final Map<String, String> options = processingEnv.getOptions();
87-
String packageName = options.get("pluginPackage");
117+
handleOptions(processingEnv.getOptions());
88118
final Messager messager = processingEnv.getMessager();
89119
messager.printMessage(Kind.NOTE, "Processing Log4j annotations");
90120
try {
91121
final Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Plugin.class);
92122
if (elements.isEmpty()) {
93123
messager.printMessage(Kind.NOTE, "No elements to process");
94-
return false;
124+
return true;
95125
}
96126
messager.printMessage(Kind.NOTE, "Retrieved " + elements.size() + " Plugin elements");
97127
final List<PluginEntry> list = new ArrayList<>();
@@ -115,14 +145,12 @@ private void error(final CharSequence message) {
115145

116146
private String collectPlugins(
117147
String packageName, final Iterable<? extends Element> elements, final List<PluginEntry> list) {
118-
final boolean calculatePackage = packageName == null;
148+
final boolean calculatePackage = packageName.isEmpty();
119149
final var pluginVisitor = new PluginElementVisitor();
120150
final var pluginAliasesVisitor = new PluginAliasesElementVisitor();
121151
for (final Element element : elements) {
122-
final Plugin plugin = element.getAnnotation(Plugin.class);
123-
if (plugin == null) {
124-
continue;
125-
}
152+
// The elements must be annotated with `Plugin`
153+
Plugin plugin = element.getAnnotation(Plugin.class);
126154
final var entry = element.accept(pluginVisitor, plugin);
127155
list.add(entry);
128156
if (calculatePackage) {
@@ -135,11 +163,11 @@ private String collectPlugins(
135163

136164
private String calculatePackage(Element element, String packageName) {
137165
final Name name = processingEnv.getElementUtils().getPackageOf(element).getQualifiedName();
138-
if (name == null) {
139-
return null;
166+
if (name.isEmpty()) {
167+
return "";
140168
}
141169
final String pkgName = name.toString();
142-
if (packageName == null) {
170+
if (packageName.isEmpty()) {
143171
return pkgName;
144172
}
145173
if (pkgName.length() == packageName.length()) {
@@ -158,6 +186,7 @@ private void writeServiceFile(final String pkgName) throws IOException {
158186
.createResource(StandardLocation.CLASS_OUTPUT, Strings.EMPTY, SERVICE_FILE_NAME);
159187
try (final PrintWriter writer =
160188
new PrintWriter(new BufferedWriter(new OutputStreamWriter(fileObject.openOutputStream(), UTF_8)))) {
189+
writer.println("# Generated by " + PluginProcessor.class.getName());
161190
writer.println(createFqcn(pkgName));
162191
}
163192
}
@@ -167,12 +196,16 @@ private void writeClassFile(final String pkg, final List<PluginEntry> list) {
167196
try (final PrintWriter writer = createSourceFile(fqcn)) {
168197
writer.println("package " + pkg + ".plugins;");
169198
writer.println("");
170-
writer.println("import aQute.bnd.annotation.Resolution;");
171-
writer.println("import aQute.bnd.annotation.spi.ServiceProvider;");
199+
if (enableBndAnnotations) {
200+
writer.println("import aQute.bnd.annotation.Resolution;");
201+
writer.println("import aQute.bnd.annotation.spi.ServiceProvider;");
202+
}
172203
writer.println("import org.apache.logging.log4j.plugins.model.PluginEntry;");
173204
writer.println("import org.apache.logging.log4j.plugins.model.PluginService;");
174205
writer.println("");
175-
writer.println("@ServiceProvider(value = PluginService.class, resolution = Resolution.OPTIONAL)");
206+
if (enableBndAnnotations) {
207+
writer.println("@ServiceProvider(value = PluginService.class, resolution = Resolution.OPTIONAL)");
208+
}
176209
writer.println("public class Log4jPlugins extends PluginService {");
177210
writer.println("");
178211
writer.println(" private static final PluginEntry[] ENTRIES = new PluginEntry[] {");
@@ -282,6 +315,25 @@ private String commonPrefix(final String str1, final String str2) {
282315
return str1.substring(0, minLength);
283316
}
284317

318+
private static boolean isServiceConsumerClassPresent() {
319+
try {
320+
Class.forName("aQute.bnd.annotation.spi.ServiceConsumer");
321+
return true;
322+
} catch (ClassNotFoundException e) {
323+
return false;
324+
}
325+
}
326+
327+
private void handleOptions(Map<String, String> options) {
328+
packageName = options.getOrDefault(PLUGIN_PACKAGE, "");
329+
String enableBndAnnotationsOption = options.get(ENABLE_BND_ANNOTATIONS);
330+
if (enableBndAnnotationsOption != null) {
331+
this.enableBndAnnotations = !"false".equals(enableBndAnnotationsOption);
332+
} else {
333+
this.enableBndAnnotations = isServiceConsumerClassPresent();
334+
}
335+
}
336+
285337
/**
286338
* ElementVisitor to scan the PluginAliases annotation.
287339
*/

0 commit comments

Comments
 (0)