From 5402f5b43f30249aa81e0fbfa53a9ebdc3e36f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Thu, 20 Aug 2026 09:29:51 +0200 Subject: [PATCH] Polish "Release file handle when comparing AOT file content" See gh-51398 --- .../logback/SpringBootJoranConfigurator.java | 11 ++-- .../SpringBootJoranConfiguratorTests.java | 63 ------------------- 2 files changed, 7 insertions(+), 67 deletions(-) diff --git a/core/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java b/core/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java index 570c09db4be..d344da6e501 100644 --- a/core/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java +++ b/core/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringBootJoranConfigurator.java @@ -442,10 +442,7 @@ class SpringBootJoranConfigurator extends JoranConfigurator { if (file.exists()) { InputStreamSource content = file.getContent(); Assert.state(content != null, "Unable to get file content"); - byte[] existingContent; - try (InputStream inputStream = content.getInputStream()) { - existingContent = inputStream.readAllBytes(); - } + byte[] existingContent = toByteArray(content); if (!Arrays.equals(this.newContent, existingContent)) { throw new IllegalStateException( "Logging configuration differs from the configuration that has already been written. " @@ -457,6 +454,12 @@ class SpringBootJoranConfigurator extends JoranConfigurator { } } + private byte[] toByteArray(InputStreamSource content) throws IOException { + try (InputStream inputStream = content.getInputStream()) { + return inputStream.readAllBytes(); + } + } + } } diff --git a/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/SpringBootJoranConfiguratorTests.java b/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/SpringBootJoranConfiguratorTests.java index afb3f7d951f..0b08bbe8ac2 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/SpringBootJoranConfiguratorTests.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/logging/logback/SpringBootJoranConfiguratorTests.java @@ -16,17 +16,10 @@ package org.springframework.boot.logging.logback; -import java.io.ByteArrayInputStream; -import java.io.FilterInputStream; -import java.io.IOException; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; import ch.qos.logback.classic.BasicConfigurator; import ch.qos.logback.classic.LoggerContext; @@ -39,12 +32,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.aot.generate.ClassNameGenerator; -import org.springframework.aot.generate.DefaultGenerationContext; -import org.springframework.aot.generate.GeneratedFiles; -import org.springframework.aot.generate.GeneratedFiles.FileHandler; import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution; -import org.springframework.beans.factory.aot.BeanFactoryInitializationCode; import org.springframework.boot.context.properties.source.ConfigurationPropertySources; import org.springframework.boot.logging.LoggingInitializationContext; import org.springframework.boot.testsupport.classpath.ClassPathExclusions; @@ -52,14 +40,10 @@ import org.springframework.boot.testsupport.classpath.resources.WithResource; import org.springframework.boot.testsupport.system.CapturedOutput; import org.springframework.boot.testsupport.system.OutputCaptureExtension; import org.springframework.context.aot.AbstractAotProcessor; -import org.springframework.core.io.InputStreamSource; -import org.springframework.javapoet.ClassName; import org.springframework.mock.env.MockEnvironment; import org.springframework.test.context.support.TestPropertySourceUtils; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.mockito.Mockito.mock; /** * Tests for {@link SpringBootJoranConfigurator}. @@ -299,29 +283,6 @@ class SpringBootJoranConfiguratorTests { }); } - @Test - @WithPropertyXmlResource - void aotContributionClosesExistingFileContent() throws Exception { - withSystemProperty(AbstractAotProcessor.AOT_PROCESSING, "true", () -> { - initialize("property.xml"); - BeanFactoryInitializationAotContribution contribution = (BeanFactoryInitializationAotContribution) this.context - .getObject(BeanFactoryInitializationAotContribution.class.getName()); - assertThat(contribution).isNotNull(); - List closed = new ArrayList<>(); - GeneratedFiles generatedFiles = (kind, path, handler) -> { - AtomicBoolean fileClosed = new AtomicBoolean(); - closed.add(fileClosed); - handler.accept(new ClosableContentFileHandler(fileClosed)); - }; - DefaultGenerationContext generationContext = new DefaultGenerationContext( - new ClassNameGenerator(ClassName.get(Object.class)), generatedFiles); - assertThatIllegalStateException() - .isThrownBy(() -> contribution.applyTo(generationContext, mock(BeanFactoryInitializationCode.class))) - .withMessageContaining("Logging configuration differs"); - assertThat(closed).isNotEmpty().allMatch(AtomicBoolean::get); - }); - } - private void withSystemProperty(String name, String value, Action action) throws Exception { System.setProperty(name, value); try { @@ -357,30 +318,6 @@ class SpringBootJoranConfiguratorTests { } - /** - * {@link FileHandler} whose existing content records when its stream is closed. - */ - private static final class ClosableContentFileHandler extends FileHandler { - - private ClosableContentFileHandler(AtomicBoolean closed) { - super(true, () -> (InputStreamSource) () -> new FilterInputStream( - new ByteArrayInputStream("existing".getBytes(StandardCharsets.UTF_8))) { - - @Override - public void close() throws IOException { - closed.set(true); - super.close(); - } - - }); - } - - @Override - protected void copy(InputStreamSource content, boolean override) { - } - - } - @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @WithResource(name = "property-default-value.xml", content = """