Skip to content

Commit 7da8450

Browse files
authored
fix: Fix Java type for annotated array parameters (#3797)
This update fixes the GraalVM reachability metadata generation for methods with annotated array parameters, such as `@Nullable String[]`. Previously, the code computed the fully qualified class name for the parameter using the **raw** type, which retained the annotations (e.g., `@org.jspecify.annotations.Nullable java.lang.String`). This resulted in incorrect metadata that was ignored by GraalVM. The issue is resolved by transforming the `DeclaredType` into a `TypeElement`, effectively removing any annotations, and then calling `getQualifiedName()` to correctly generate the fully qualified class name without annotations.
1 parent c4df56e commit 7da8450

File tree

4 files changed

+81
-5
lines changed

4 files changed

+81
-5
lines changed

log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/plugins/processor/GraalVmProcessorTest.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ class GraalVmProcessorTest {
103103
private static final Object FAKE_CONSTRAINT_VALIDATOR = onlyNoArgsConstructor(FAKE_CONSTRAINT_VALIDATOR_NAME);
104104
private static final String FAKE_PLUGIN_VISITOR_NAME = "example.FakeAnnotations$FakePluginVisitor";
105105
private static final Object FAKE_PLUGIN_VISITOR = onlyNoArgsConstructor(FAKE_PLUGIN_VISITOR_NAME);
106+
private static final String FAKE_CONVERTER_NAME = "example.FakeConverter";
107+
private static final Object FAKE_CONVERTER = asMap(
108+
"name",
109+
FAKE_CONVERTER_NAME,
110+
"methods",
111+
singletonList(asMap(
112+
"name",
113+
"newInstance",
114+
"parameterTypes",
115+
asList("org.apache.logging.log4j.core.config.Configuration", "java.lang.String[]"))),
116+
"fields",
117+
emptyList());
106118

107119
private static final String GROUP_ID = "groupId";
108120
private static final String ARTIFACT_ID = "artifactId";
@@ -155,7 +167,8 @@ static Stream<Arguments> containsSpecificEntries() {
155167
Arguments.of(FAKE_PLUGIN_BUILDER_NAME, FAKE_PLUGIN_BUILDER),
156168
Arguments.of(FAKE_PLUGIN_NESTED_NAME, FAKE_PLUGIN_NESTED),
157169
Arguments.of(FAKE_CONSTRAINT_VALIDATOR_NAME, FAKE_CONSTRAINT_VALIDATOR),
158-
Arguments.of(FAKE_PLUGIN_VISITOR_NAME, FAKE_PLUGIN_VISITOR));
170+
Arguments.of(FAKE_PLUGIN_VISITOR_NAME, FAKE_PLUGIN_VISITOR),
171+
Arguments.of(FAKE_CONVERTER_NAME, FAKE_CONVERTER));
159172
}
160173

161174
@ParameterizedTest
@@ -168,7 +181,9 @@ void containsSpecificEntries(String className, Object expectedJson) throws IOExc
168181
assertThatJson(reachabilityMetadata)
169182
.inPath(String.format("$[?(@.name == '%s')]", className))
170183
.isArray()
171-
.contains(json(expectedJson));
184+
.hasSize(1)
185+
.first()
186+
.isEqualTo(json(expectedJson));
172187
}
173188

174189
static Stream<Arguments> reachabilityMetadataPath() {
@@ -214,7 +229,7 @@ void whenNoGroupIdAndArtifactId_thenWarningIsPrinted(@TempDir(cleanup = CleanupM
214229
}
215230
// The generated folder name should be deterministic and based solely on the descriptor content.
216231
// If the descriptor changes, this test and the expected folder name must be updated accordingly.
217-
assertThat(reachabilityMetadataFolders).hasSize(1).containsExactly(path.resolve("62162090"));
232+
assertThat(reachabilityMetadataFolders).hasSize(1).containsExactly(path.resolve("72c240aa"));
218233
assertThat(reachabilityMetadataFolders.get(0).resolve("reflect-config.json"))
219234
.as("Reachability metadata file")
220235
.exists();
@@ -250,7 +265,6 @@ private static List<String> generateDescriptor(
250265
}
251266

252267
// Compile the sources
253-
final Path descriptorFilePath = outputDir.resolve("plugins.xml");
254268
final DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
255269
final JavaCompiler.CompilationTask task =
256270
compiler.getTask(null, fileManager, diagnosticCollector, options, null, sources);
@@ -260,6 +274,8 @@ private static List<String> generateDescriptor(
260274
return diagnosticCollector.getDiagnostics().stream()
261275
.filter(d -> d.getKind() != Diagnostic.Kind.NOTE)
262276
.map(d -> d.getMessage(Locale.ROOT))
277+
// This message appears when the test runs on JDK 8
278+
.filter(m -> !"unknown enum constant java.lang.annotation.ElementType.MODULE".equals(m))
263279
.collect(Collectors.toList());
264280
}
265281
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package example;
18+
19+
import org.apache.logging.log4j.core.LogEvent;
20+
import org.apache.logging.log4j.core.config.Configuration;
21+
import org.apache.logging.log4j.core.config.plugins.Plugin;
22+
import org.apache.logging.log4j.core.pattern.ConverterKeys;
23+
import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
24+
import org.apache.logging.log4j.core.pattern.PatternConverter;
25+
import org.jspecify.annotations.NullMarked;
26+
import org.jspecify.annotations.Nullable;
27+
28+
@NullMarked
29+
@Plugin(name = "FakePatternConverter", category = PatternConverter.CATEGORY)
30+
@ConverterKeys({"f", "fake"})
31+
public final class FakeConverter extends LogEventPatternConverter {
32+
33+
private FakeConverter(@Nullable final Configuration config, @Nullable final String[] options) {
34+
super("Fake", "fake");
35+
}
36+
37+
public static FakeConverter newInstance(
38+
@Nullable final Configuration config, @Nullable final String[] options) {
39+
return new FakeConverter(config, options);
40+
}
41+
42+
@Override
43+
public void format(LogEvent event, StringBuilder toAppendTo) {
44+
toAppendTo.append("FakeConverter: ").append(event.getMessage().getFormattedMessage());
45+
}
46+
}

log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/GraalVmProcessor.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,9 @@ public String visitArray(final ArrayType t, @Nullable Void unused) {
300300

301301
@Override
302302
public @Nullable String visitDeclared(final DeclaredType t, final Void unused) {
303-
return processingEnv.getTypeUtils().erasure(t).toString();
303+
return safeCast(t.asElement(), TypeElement.class)
304+
.getQualifiedName()
305+
.toString();
304306
}
305307
},
306308
null);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<entry xmlns="https://logging.apache.org/xml/ns"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="
5+
https://logging.apache.org/xml/ns
6+
https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
7+
type="fixed">
8+
<issue id="3796" link="https://github.com/apache/logging-log4j2/issues/3796"/>
9+
<description format="asciidoc">
10+
Fix GraalVM reachability metadata generation for methods with annotated array type parameters, such as `@Nullable String[]`.
11+
</description>
12+
</entry>

0 commit comments

Comments
 (0)