Skip to content

Commit 660dbb9

Browse files
committed
Fix Maven-built native images with Docker Compose dependency
Previously, we tried to prevent spring-boot-docker-compose from causing problems with AOT and native images by excluding it from the AOT processing classpath. This allowed AOT processing to succeed. We cannot apply the same exclusion to the native image classpath so spring-boot-docker-compose was still included in the native image. This results in a failure at runtime due to missing reflection hints. This commit reverts that changes that excluded spring-boot-docker-compose from the AOT processing classpath. This allows AOT processing to generate reflection hints but reintroduces the failure caused by the connection details bean definitions using an instance supplier callback. To overcome this problem we disable DockerComposeLifecycleManager during AOT processing and in a native image. This ensures that no attempt is made to call docker compose up and no connection details beans are defined. Fixes gh-35676
1 parent 06604ef commit 660dbb9

File tree

6 files changed

+47
-96
lines changed

6 files changed

+47
-96
lines changed

spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManager.java

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.commons.logging.Log;
2525
import org.apache.commons.logging.LogFactory;
2626

27+
import org.springframework.aot.AotDetector;
2728
import org.springframework.boot.SpringApplicationShutdownHandlers;
2829
import org.springframework.boot.context.properties.bind.Binder;
2930
import org.springframework.boot.docker.compose.core.DockerCompose;
@@ -92,6 +93,10 @@ class DockerComposeLifecycleManager {
9293
}
9394

9495
void start() {
96+
if (Boolean.getBoolean("spring.aot.processing") || AotDetector.useGeneratedArtifacts()) {
97+
logger.trace("Docker Compose support disabled with AOT and native images");
98+
return;
99+
}
95100
if (!this.properties.isEnabled()) {
96101
logger.trace("Docker Compose support not enabled");
97102
return;

spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManagerTests.java

+41
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.junit.jupiter.api.Test;
3232
import org.junit.jupiter.api.io.TempDir;
3333

34+
import org.springframework.aot.AotDetector;
3435
import org.springframework.boot.SpringApplicationShutdownHandlers;
3536
import org.springframework.boot.context.properties.bind.Binder;
3637
import org.springframework.boot.docker.compose.core.DockerCompose;
@@ -116,6 +117,30 @@ void startWhenEnabledFalseDoesNotStart() {
116117
then(this.dockerCompose).should(never()).hasDefinedServices();
117118
}
118119

120+
@Test
121+
void startWhenAotProcessingDoesNotStart() {
122+
withSystemProperty("spring.aot.processing", "true", () -> {
123+
EventCapturingListener listener = new EventCapturingListener();
124+
this.eventListeners.add(listener);
125+
setUpRunningServices();
126+
this.lifecycleManager.start();
127+
assertThat(listener.getEvent()).isNull();
128+
then(this.dockerCompose).should(never()).hasDefinedServices();
129+
});
130+
}
131+
132+
@Test
133+
void startWhenUsingAotArtifactsDoesNotStart() {
134+
withSystemProperty(AotDetector.AOT_ENABLED, "true", () -> {
135+
EventCapturingListener listener = new EventCapturingListener();
136+
this.eventListeners.add(listener);
137+
setUpRunningServices();
138+
this.lifecycleManager.start();
139+
assertThat(listener.getEvent()).isNull();
140+
then(this.dockerCompose).should(never()).hasDefinedServices();
141+
});
142+
}
143+
119144
@Test
120145
void startWhenComposeFileNotFoundThrowsException() {
121146
DockerComposeLifecycleManager manager = new DockerComposeLifecycleManager(new File("."),
@@ -362,6 +387,22 @@ private void setUpRunningServices(boolean started, Map<String, String> labels) {
362387
}
363388
}
364389

390+
private void withSystemProperty(String key, String value, Runnable action) {
391+
String previous = System.getProperty(key);
392+
try {
393+
System.setProperty(key, value);
394+
action.run();
395+
}
396+
finally {
397+
if (previous == null) {
398+
System.clearProperty(key);
399+
}
400+
else {
401+
System.setProperty(key, previous);
402+
}
403+
}
404+
}
405+
365406
/**
366407
* Testable {@link SpringApplicationShutdownHandlers}.
367408
*/

spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/AotTests.java

-9
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,6 @@ void whenAotTestRunsSourcesAndResourcesAreGenerated(MavenBuild mavenBuild) {
176176
});
177177
}
178178

179-
@TestTemplate
180-
void whenAotWithDevelopmentOnlyExclusions(MavenBuild mavenBuild) {
181-
mavenBuild.project("aot-development-only-exclusions").goals("package").execute((project) -> {
182-
Path aotDirectory = project.toPath().resolve("target/spring-aot/main");
183-
assertThat(collectRelativePaths(aotDirectory.resolve("sources")))
184-
.contains(Path.of("org", "test", "SampleApplication__ApplicationContextInitializer.java"));
185-
});
186-
}
187-
188179
List<Path> collectRelativePaths(Path sourceDirectory) {
189180
try (Stream<Path> pathStream = Files.walk(sourceDirectory)) {
190181
return pathStream.filter(Files::isRegularFile)

spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/aot-development-only-exclusions/pom.xml

-54
This file was deleted.

spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/aot-development-only-exclusions/src/main/java/org/test/SampleApplication.java

-32
This file was deleted.

spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ProcessAotMojo.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ private String[] getAotArguments(String applicationClass) {
111111

112112
private URL[] getClassPath() throws Exception {
113113
File[] directories = new File[] { this.classesDirectory, this.generatedClasses };
114-
return getClassPath(directories, new ExcludeTestScopeArtifactFilter(), DOCKER_COMPOSE_EXCLUDE_FILTER);
114+
return getClassPath(directories, new ExcludeTestScopeArtifactFilter());
115115
}
116116

117117
private RunArguments resolveArguments() {

0 commit comments

Comments
 (0)