From 660dbb9afdaaa128e839720d55dbb10fa87194b7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 21 Jun 2023 11:33:15 +0100 Subject: [PATCH] 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 --- .../DockerComposeLifecycleManager.java | 5 ++ .../DockerComposeLifecycleManagerTests.java | 41 ++++++++++++++ .../springframework/boot/maven/AotTests.java | 9 ---- .../aot-development-only-exclusions/pom.xml | 54 ------------------- .../main/java/org/test/SampleApplication.java | 32 ----------- .../boot/maven/ProcessAotMojo.java | 2 +- 6 files changed, 47 insertions(+), 96 deletions(-) delete mode 100644 spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/aot-development-only-exclusions/pom.xml delete mode 100644 spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/aot-development-only-exclusions/src/main/java/org/test/SampleApplication.java diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManager.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManager.java index a5db68388675..4fbf7b1b63da 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManager.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManager.java @@ -24,6 +24,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.aot.AotDetector; import org.springframework.boot.SpringApplicationShutdownHandlers; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.docker.compose.core.DockerCompose; @@ -92,6 +93,10 @@ class DockerComposeLifecycleManager { } void start() { + if (Boolean.getBoolean("spring.aot.processing") || AotDetector.useGeneratedArtifacts()) { + logger.trace("Docker Compose support disabled with AOT and native images"); + return; + } if (!this.properties.isEnabled()) { logger.trace("Docker Compose support not enabled"); return; diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManagerTests.java b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManagerTests.java index 72d3dcbcedae..f3a13c17c3d6 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManagerTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManagerTests.java @@ -31,6 +31,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.springframework.aot.AotDetector; import org.springframework.boot.SpringApplicationShutdownHandlers; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.docker.compose.core.DockerCompose; @@ -116,6 +117,30 @@ void startWhenEnabledFalseDoesNotStart() { then(this.dockerCompose).should(never()).hasDefinedServices(); } + @Test + void startWhenAotProcessingDoesNotStart() { + withSystemProperty("spring.aot.processing", "true", () -> { + EventCapturingListener listener = new EventCapturingListener(); + this.eventListeners.add(listener); + setUpRunningServices(); + this.lifecycleManager.start(); + assertThat(listener.getEvent()).isNull(); + then(this.dockerCompose).should(never()).hasDefinedServices(); + }); + } + + @Test + void startWhenUsingAotArtifactsDoesNotStart() { + withSystemProperty(AotDetector.AOT_ENABLED, "true", () -> { + EventCapturingListener listener = new EventCapturingListener(); + this.eventListeners.add(listener); + setUpRunningServices(); + this.lifecycleManager.start(); + assertThat(listener.getEvent()).isNull(); + then(this.dockerCompose).should(never()).hasDefinedServices(); + }); + } + @Test void startWhenComposeFileNotFoundThrowsException() { DockerComposeLifecycleManager manager = new DockerComposeLifecycleManager(new File("."), @@ -362,6 +387,22 @@ private void setUpRunningServices(boolean started, Map labels) { } } + private void withSystemProperty(String key, String value, Runnable action) { + String previous = System.getProperty(key); + try { + System.setProperty(key, value); + action.run(); + } + finally { + if (previous == null) { + System.clearProperty(key); + } + else { + System.setProperty(key, previous); + } + } + } + /** * Testable {@link SpringApplicationShutdownHandlers}. */ diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/AotTests.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/AotTests.java index 9d21d9aad71f..f2052158985f 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/AotTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/AotTests.java @@ -176,15 +176,6 @@ void whenAotTestRunsSourcesAndResourcesAreGenerated(MavenBuild mavenBuild) { }); } - @TestTemplate - void whenAotWithDevelopmentOnlyExclusions(MavenBuild mavenBuild) { - mavenBuild.project("aot-development-only-exclusions").goals("package").execute((project) -> { - Path aotDirectory = project.toPath().resolve("target/spring-aot/main"); - assertThat(collectRelativePaths(aotDirectory.resolve("sources"))) - .contains(Path.of("org", "test", "SampleApplication__ApplicationContextInitializer.java")); - }); - } - List collectRelativePaths(Path sourceDirectory) { try (Stream pathStream = Files.walk(sourceDirectory)) { return pathStream.filter(Files::isRegularFile) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/aot-development-only-exclusions/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/aot-development-only-exclusions/pom.xml deleted file mode 100644 index 3620e07aa003..000000000000 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/aot-development-only-exclusions/pom.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - 4.0.0 - org.springframework.boot.maven.it - aot-development-only-exclusions - 0.0.1.BUILD-SNAPSHOT - - UTF-8 - @java.version@ - @java.version@ - - - - - @project.groupId@ - @project.artifactId@ - @project.version@ - - - - process-aot - - - - - - - - - org.springframework.boot - spring-boot - @project.version@ - - - jakarta.servlet - jakarta.servlet-api - @jakarta-servlet.version@ - provided - - - org.springframework.boot - spring-boot-devtools - @project.version@ - true - - - org.springframework.boot - spring-boot-docker-compose - @project.version@ - true - - - diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/aot-development-only-exclusions/src/main/java/org/test/SampleApplication.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/aot-development-only-exclusions/src/main/java/org/test/SampleApplication.java deleted file mode 100644 index cd19b00cf6b7..000000000000 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/aot-development-only-exclusions/src/main/java/org/test/SampleApplication.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2012-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.test; - -import org.springframework.boot.SpringApplication; -import org.springframework.context.annotation.Configuration; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; - -@Configuration(proxyBeanMethods = false) -public class SampleApplication { - - public static void main(String[] args) { - Assert.state(!ClassUtils.isPresent("org.springframework.boot.docker.compose.core.DockerCompose", null), "Should not have docker-compose"); - SpringApplication.run(SampleApplication.class, args); - } - -} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ProcessAotMojo.java b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ProcessAotMojo.java index f5f3f2837bd7..44cd248c4a36 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ProcessAotMojo.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/ProcessAotMojo.java @@ -111,7 +111,7 @@ private String[] getAotArguments(String applicationClass) { private URL[] getClassPath() throws Exception { File[] directories = new File[] { this.classesDirectory, this.generatedClasses }; - return getClassPath(directories, new ExcludeTestScopeArtifactFilter(), DOCKER_COMPOSE_EXCLUDE_FILTER); + return getClassPath(directories, new ExcludeTestScopeArtifactFilter()); } private RunArguments resolveArguments() {