Skip to content

Commit e20e239

Browse files
committed
Polish "Add runtime hints for Log4j Core 2"
See gh-46410
1 parent a2d4ecf commit e20e239

File tree

3 files changed

+67
-52
lines changed

3 files changed

+67
-52
lines changed

core/spring-boot/build.gradle

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,6 @@ dependencies {
7171
testImplementation("org.hibernate.validator:hibernate-validator")
7272
testImplementation("org.jboss.logging:jboss-logging")
7373
testImplementation("org.springframework.data:spring-data-r2dbc")
74-
75-
// Used in Log4J2RuntimeHintsTests
76-
testRuntimeOnly("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml")
7774
}
7875

7976
def syncJavaTemplates = tasks.register("syncJavaTemplates", Sync) {
Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2025-present the original author or authors.
2+
* Copyright 2012-present the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,42 +26,33 @@
2626
* {@link RuntimeHintsRegistrar} implementation for {@link Log4J2LoggingSystem}.
2727
*
2828
* @author Piotr P. Karwasz
29+
* @author Stephane Nicoll
2930
*/
3031
class Log4J2RuntimeHints implements RuntimeHintsRegistrar {
3132

3233
@Override
3334
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
34-
if (!ClassUtils.isPresent(Log4J2LoggingSystem.Factory.LOG4J_CORE_CONTEXT_FACTORY, classLoader)) {
35-
return;
35+
if (ClassUtils.isPresent(Log4J2LoggingSystem.Factory.LOG4J_CORE_CONTEXT_FACTORY, classLoader)) {
36+
registerLog4j2Hints(hints, classLoader);
3637
}
37-
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.Factory.LOG4J_CORE_CONTEXT_FACTORY);
38+
}
39+
40+
private void registerLog4j2Hints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
41+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.Factory.LOG4J_CORE_CONTEXT_FACTORY);
3842
// Register default Log4j2 configuration files
3943
hints.resources().registerPattern("org/springframework/boot/logging/log4j2/log4j2.xml");
4044
hints.resources().registerPattern("org/springframework/boot/logging/log4j2/log4j2-file.xml");
4145
hints.resources().registerPattern("log4j2.springboot");
4246
// Declares the types that Log4j2LoggingSystem checks for existence reflectively.
43-
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.JSON_TREE_PARSER_V2);
44-
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.JSON_TREE_PARSER_V3);
45-
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.PROPS_CONFIGURATION_FACTORY_V2);
46-
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.PROPS_CONFIGURATION_FACTORY_V3);
47-
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.YAML_TREE_PARSER_V2);
48-
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.YAML_CONFIGURATION_FACTORY_V2);
49-
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.YAML_CONFIGURATION_FACTORY_V3);
50-
// Register JUL to Log4j 2 bridge handler
51-
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.LOG4J_BRIDGE_HANDLER);
52-
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.LOG4J_LOG_MANAGER);
53-
// Don't need to register the custom Log4j 2 plugins,
54-
// since they will be registered by the Log4j 2 `GraalvmPluginProcessor`.
55-
}
56-
57-
/**
58-
* Registers the type to prevent GraalVM from removing it during the native build.
59-
* @param hints the runtime hints to register with
60-
* @param classLoader the class loader to use for type resolution
61-
* @param typeName the name of the type to register
62-
*/
63-
private void registerTypeForReachability(RuntimeHints hints, @Nullable ClassLoader classLoader, String typeName) {
64-
hints.reflection().registerTypeIfPresent(classLoader, typeName);
47+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.JSON_TREE_PARSER_V2);
48+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.JSON_TREE_PARSER_V3);
49+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.PROPS_CONFIGURATION_FACTORY_V2);
50+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.PROPS_CONFIGURATION_FACTORY_V3);
51+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.YAML_TREE_PARSER_V2);
52+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.YAML_CONFIGURATION_FACTORY_V2);
53+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.YAML_CONFIGURATION_FACTORY_V3);
54+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.LOG4J_BRIDGE_HANDLER);
55+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.LOG4J_LOG_MANAGER);
6556
}
6657

6758
}

core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2RuntimeHintsTests.java

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,58 +16,85 @@
1616

1717
package org.springframework.boot.logging.log4j2;
1818

19+
import java.net.URL;
20+
import java.net.URLClassLoader;
21+
import java.util.Arrays;
22+
23+
import org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory;
24+
import org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory;
1925
import org.apache.logging.log4j.core.impl.Log4jContextFactory;
2026
import org.apache.logging.log4j.jul.Log4jBridgeHandler;
2127
import org.apache.logging.log4j.jul.LogManager;
2228
import org.junit.jupiter.api.Test;
2329

24-
import org.springframework.aot.hint.ReflectionHints;
2530
import org.springframework.aot.hint.RuntimeHints;
26-
import org.springframework.aot.hint.TypeReference;
31+
import org.springframework.aot.hint.predicate.ReflectionHintsPredicates;
32+
import org.springframework.aot.hint.predicate.ResourceHintsPredicates;
33+
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
2734

2835
import static org.assertj.core.api.Assertions.assertThat;
2936

3037
/**
3138
* Tests for {@link Log4J2RuntimeHints}.
3239
*
3340
* @author Piotr P. Karwasz
41+
* @author Stephane Nicoll
3442
*/
3543
class Log4J2RuntimeHintsTests {
3644

45+
private static final ReflectionHintsPredicates reflectionHints = RuntimeHintsPredicates.reflection();
46+
47+
private static final ResourceHintsPredicates resourceHints = RuntimeHintsPredicates.resource();
48+
3749
@Test
3850
void registersHintsForTypesCheckedByLog4J2LoggingSystem() {
39-
ReflectionHints reflection = registerHints();
40-
// Once Log4j Core is reachable, GraalVM will automatically
41-
// add reachability metadata embedded in the Log4j Core jar and extensions.
42-
assertThat(reflection.getTypeHint(Log4jContextFactory.class)).isNotNull();
43-
assertThat(reflection.getTypeHint(Log4jBridgeHandler.class)).isNotNull();
44-
assertThat(reflection.getTypeHint(LogManager.class)).isNotNull();
51+
RuntimeHints runtimeHints = registerHints();
52+
assertThat(reflectionHints.onType(Log4jContextFactory.class)).accepts(runtimeHints);
53+
assertThat(reflectionHints.onType(Log4jBridgeHandler.class)).accepts(runtimeHints);
54+
assertThat(reflectionHints.onType(LogManager.class)).accepts(runtimeHints);
55+
assertThat(reflectionHints.onType(PropertiesConfigurationFactory.class)).accepts(runtimeHints);
56+
assertThat(reflectionHints.onType(YamlConfigurationFactory.class)).accepts(runtimeHints);
4557
}
4658

47-
/**
48-
*
49-
*/
5059
@Test
51-
void registersHintsForConfigurationFileParsers() {
52-
ReflectionHints reflection = registerHints();
53-
// JSON
54-
assertThat(reflection.getTypeHint(TypeReference.of("com.fasterxml.jackson.databind.ObjectMapper"))).isNotNull();
55-
// YAML
56-
assertThat(reflection.getTypeHint(TypeReference.of("com.fasterxml.jackson.dataformat.yaml.YAMLMapper")))
57-
.isNotNull();
60+
void registersHintsForLog4j2DefaultConfigurationFiles() {
61+
RuntimeHints runtimeHints = registerHints();
62+
assertThat(resourceHints.forResource("org/springframework/boot/logging/log4j2/log4j2.xml"))
63+
.accepts(runtimeHints);
64+
assertThat(resourceHints.forResource("org/springframework/boot/logging/log4j2/log4j2-file.xml"))
65+
.accepts(runtimeHints);
5866
}
5967

6068
@Test
6169
void doesNotRegisterHintsWhenLog4jCoreIsNotAvailable() {
62-
RuntimeHints hints = new RuntimeHints();
63-
new Log4J2RuntimeHints().registerHints(hints, ClassLoader.getPlatformClassLoader());
64-
assertThat(hints.reflection().typeHints()).isEmpty();
70+
RuntimeHints runtimeHints = new RuntimeHints();
71+
new Log4J2RuntimeHints().registerHints(runtimeHints, new HidePackagesClassLoader("org.apache.logging.log4j"));
72+
assertThat(runtimeHints.reflection().typeHints()).isEmpty();
6573
}
6674

67-
private ReflectionHints registerHints() {
75+
private RuntimeHints registerHints() {
6876
RuntimeHints hints = new RuntimeHints();
6977
new Log4J2RuntimeHints().registerHints(hints, getClass().getClassLoader());
70-
return hints.reflection();
78+
return hints;
79+
}
80+
81+
static final class HidePackagesClassLoader extends URLClassLoader {
82+
83+
private final String[] hiddenPackages;
84+
85+
HidePackagesClassLoader(String... hiddenPackages) {
86+
super(new URL[0], HidePackagesClassLoader.class.getClassLoader());
87+
this.hiddenPackages = hiddenPackages;
88+
}
89+
90+
@Override
91+
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
92+
if (Arrays.stream(this.hiddenPackages).anyMatch(name::startsWith)) {
93+
throw new ClassNotFoundException();
94+
}
95+
return super.loadClass(name, resolve);
96+
}
97+
7198
}
7299

73100
}

0 commit comments

Comments
 (0)