Skip to content

Commit 4473570

Browse files
zarkparkacademey
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 <[email protected]>
1 parent 8a5635d commit 4473570

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed

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

Lines changed: 13 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;
@@ -27,8 +30,10 @@
2730
import org.springframework.ai.chat.messages.UserMessage;
2831
import org.springframework.ai.tool.ToolCallback;
2932
import org.springframework.ai.tool.definition.ToolDefinition;
33+
import org.springframework.aot.hint.MemberCategory;
3034
import org.springframework.aot.hint.RuntimeHints;
3135
import org.springframework.aot.hint.RuntimeHintsRegistrar;
36+
import org.springframework.aot.hint.TypeReference;
3237
import org.springframework.core.io.ClassPathResource;
3338
import org.springframework.lang.NonNull;
3439
import org.springframework.lang.Nullable;
@@ -54,6 +59,14 @@ public void registerHints(@NonNull RuntimeHints hints, @Nullable ClassLoader cla
5459
hints.resources().registerResource(new ClassPathResource(r));
5560
}
5661

62+
// Register SLF4J types for Java 22 native compilation compatibility
63+
var slf4jTypes = Set.of(NOP_FallbackServiceProvider.class, SubstituteServiceProvider.class,
64+
LoggerFactory.class);
65+
for (var c : slf4jTypes) {
66+
hints.reflection().registerType(TypeReference.of(c), MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
67+
MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.DECLARED_FIELDS);
68+
}
69+
5770
}
5871

5972
}
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)