Skip to content

Commit 2cb1daa

Browse files
academeyericbottard
authored andcommitted
Fix GraalVM native compilation issue with Java 22
This commit resolves the native compilation failure when using Java 22 with GraalVM. The issue was caused by SLF4J service providers being initialized at runtime instead of build time. Changes: - Add SLF4J RuntimeHints to SpringAiCoreRuntimeHints for native compilation - Register NOP_FallbackServiceProvider and SubstituteServiceProvider for build-time initialization - Add comprehensive tests for all registered types including SLF4J This fix ensures compatibility with both Java 21 and Java 22 for native image compilation. Fixes #494 Signed-off-by: academey <academey@gmail.com>
1 parent 2c8c4e7 commit 2cb1daa

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

spring-ai-model/src/main/java/org/springframework/ai/aot/SpringAiCoreRuntimeHints.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919
import java.util.Set;
2020

21+
import org.slf4j.LoggerFactory;
22+
import org.slf4j.helpers.NOP_FallbackServiceProvider;
23+
import org.slf4j.helpers.SubstituteServiceProvider;
2124
import org.springframework.ai.chat.messages.AbstractMessage;
2225
import org.springframework.ai.chat.messages.AssistantMessage;
2326
import org.springframework.ai.chat.messages.Message;
@@ -32,6 +35,7 @@
3235
import org.springframework.aot.hint.MemberCategory;
3336
import org.springframework.aot.hint.RuntimeHints;
3437
import org.springframework.aot.hint.RuntimeHintsRegistrar;
38+
import org.springframework.aot.hint.TypeReference;
3539
import org.springframework.core.io.ClassPathResource;
3640
import org.springframework.lang.NonNull;
3741
import org.springframework.lang.Nullable;
@@ -59,6 +63,14 @@ public void registerHints(@NonNull RuntimeHints hints, @Nullable ClassLoader cla
5963
hints.resources().registerResource(new ClassPathResource(r));
6064
}
6165

66+
// Register SLF4J types for Java 22 native compilation compatibility
67+
var slf4jTypes = Set.of(NOP_FallbackServiceProvider.class, SubstituteServiceProvider.class,
68+
LoggerFactory.class);
69+
for (var c : slf4jTypes) {
70+
hints.reflection().registerType(TypeReference.of(c), MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
71+
MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.DECLARED_FIELDS);
72+
}
73+
6274
}
6375

6476
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright 2023-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ai.aot;
18+
19+
import org.junit.jupiter.api.Test;
20+
import org.slf4j.LoggerFactory;
21+
import org.slf4j.helpers.NOP_FallbackServiceProvider;
22+
import org.slf4j.helpers.SubstituteServiceProvider;
23+
import org.springframework.ai.chat.messages.AbstractMessage;
24+
import org.springframework.ai.chat.messages.AssistantMessage;
25+
import org.springframework.ai.chat.messages.Message;
26+
import org.springframework.ai.chat.messages.MessageType;
27+
import org.springframework.ai.chat.messages.SystemMessage;
28+
import org.springframework.ai.chat.messages.ToolResponseMessage;
29+
import org.springframework.ai.chat.messages.UserMessage;
30+
import org.springframework.ai.tool.ToolCallback;
31+
import org.springframework.ai.tool.definition.ToolDefinition;
32+
import org.springframework.aot.hint.RuntimeHints;
33+
import org.springframework.aot.hint.TypeReference;
34+
35+
import static org.assertj.core.api.Assertions.assertThat;
36+
37+
/**
38+
* Tests for {@link SpringAiCoreRuntimeHints}.
39+
*
40+
* @author Christian Tzolov
41+
* @author Hyunjoon Park
42+
*/
43+
class SpringAiCoreRuntimeHintsTests {
44+
45+
@Test
46+
void registerHints() {
47+
RuntimeHints runtimeHints = new RuntimeHints();
48+
SpringAiCoreRuntimeHints springAiCoreRuntimeHints = new SpringAiCoreRuntimeHints();
49+
springAiCoreRuntimeHints.registerHints(runtimeHints, null);
50+
51+
// Verify chat message types are registered
52+
assertThat(runtimeHints.reflection().typeHints())
53+
.anySatisfy(typeHint -> assertThat(typeHint.getType())
54+
.isEqualTo(TypeReference.of(AbstractMessage.class)));
55+
assertThat(runtimeHints.reflection().typeHints())
56+
.anySatisfy(typeHint -> assertThat(typeHint.getType())
57+
.isEqualTo(TypeReference.of(AssistantMessage.class)));
58+
assertThat(runtimeHints.reflection().typeHints())
59+
.anySatisfy(typeHint -> assertThat(typeHint.getType())
60+
.isEqualTo(TypeReference.of(Message.class)));
61+
assertThat(runtimeHints.reflection().typeHints())
62+
.anySatisfy(typeHint -> assertThat(typeHint.getType())
63+
.isEqualTo(TypeReference.of(MessageType.class)));
64+
assertThat(runtimeHints.reflection().typeHints())
65+
.anySatisfy(typeHint -> assertThat(typeHint.getType())
66+
.isEqualTo(TypeReference.of(SystemMessage.class)));
67+
assertThat(runtimeHints.reflection().typeHints())
68+
.anySatisfy(typeHint -> assertThat(typeHint.getType())
69+
.isEqualTo(TypeReference.of(ToolResponseMessage.class)));
70+
assertThat(runtimeHints.reflection().typeHints())
71+
.anySatisfy(typeHint -> assertThat(typeHint.getType())
72+
.isEqualTo(TypeReference.of(UserMessage.class)));
73+
74+
// Verify tool types are registered
75+
assertThat(runtimeHints.reflection().typeHints())
76+
.anySatisfy(typeHint -> assertThat(typeHint.getType())
77+
.isEqualTo(TypeReference.of(ToolCallback.class)));
78+
assertThat(runtimeHints.reflection().typeHints())
79+
.anySatisfy(typeHint -> assertThat(typeHint.getType())
80+
.isEqualTo(TypeReference.of(ToolDefinition.class)));
81+
82+
// Verify SLF4J types are registered for Java 22 compatibility
83+
assertThat(runtimeHints.reflection().typeHints())
84+
.anySatisfy(typeHint -> assertThat(typeHint.getType())
85+
.isEqualTo(TypeReference.of(NOP_FallbackServiceProvider.class)));
86+
assertThat(runtimeHints.reflection().typeHints())
87+
.anySatisfy(typeHint -> assertThat(typeHint.getType())
88+
.isEqualTo(TypeReference.of(SubstituteServiceProvider.class)));
89+
assertThat(runtimeHints.reflection().typeHints())
90+
.anySatisfy(typeHint -> assertThat(typeHint.getType())
91+
.isEqualTo(TypeReference.of(LoggerFactory.class)));
92+
93+
// Verify resources are registered
94+
assertThat(runtimeHints.resources().resourcePatternHints())
95+
.anySatisfy(hint -> assertThat(hint.getIncludes())
96+
.anyMatch(include -> include.getPattern().contains("embedding-model-dimensions.properties")));
97+
}
98+
}

0 commit comments

Comments
 (0)