From 1b1c61a2ed66d41ee3e8679ba1bef72aa1d75dc7 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 13 Dec 2019 13:04:49 -0800 Subject: [PATCH] Make processor output fully reproducible Update `AutoConfigureAnnotationProcessor` to ensure that the generated properties file is fully repeatable. Properties are now sorted and written out directly to ensure that the timestamp comment is not present. Closes gh-19370 --- .../AutoConfigureAnnotationProcessor.java | 22 +++++++++++++------ ...AutoConfigureAnnotationProcessorTests.java | 16 +++++++++++++- .../TestAutoConfigureAnnotationProcessor.java | 6 ++++- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/main/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessor.java b/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/main/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessor.java index 5656396a1bc..0a1500a8f11 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/main/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessor.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/main/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessor.java @@ -17,7 +17,9 @@ package org.springframework.boot.autoconfigureprocessor; import java.io.IOException; -import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -26,11 +28,12 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Properties; import java.util.Set; +import java.util.TreeMap; import java.util.stream.Stream; import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Filer; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; @@ -66,7 +69,7 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor { private final Map valueExtractors; - private final Properties properties = new Properties(); + private final Map properties = new TreeMap<>(); public AutoConfigureAnnotationProcessor() { Map annotations = new LinkedHashMap<>(); @@ -177,10 +180,15 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor { private void writeProperties() throws IOException { if (!this.properties.isEmpty()) { - FileObject file = this.processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", - PROPERTIES_PATH); - try (OutputStream outputStream = file.openOutputStream()) { - this.properties.store(outputStream, null); + Filer filer = this.processingEnv.getFiler(); + FileObject file = filer.createResource(StandardLocation.CLASS_OUTPUT, "", PROPERTIES_PATH); + try (Writer writer = new OutputStreamWriter(file.openOutputStream(), StandardCharsets.UTF_8)) { + for (Map.Entry entry : this.properties.entrySet()) { + writer.append(entry.getKey()); + writer.append("="); + writer.append(entry.getValue()); + writer.append(System.lineSeparator()); + } } } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessorTests.java b/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessorTests.java index ea6a7c1d974..49873ec4fea 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessorTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessorTests.java @@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.springframework.boot.testsupport.compiler.TestCompiler; +import org.springframework.util.FileCopyUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -96,11 +97,24 @@ class AutoConfigureAnnotationProcessorTests { "123"); } + @Test // gh-19370 + void propertiesAreFullRepeatable() throws Exception { + String first = new String( + FileCopyUtils.copyToByteArray(process(TestOrderedClassConfiguration.class).getWrittenFile())); + String second = new String( + FileCopyUtils.copyToByteArray(process(TestOrderedClassConfiguration.class).getWrittenFile())); + assertThat(first).isEqualTo(second).doesNotContain("#"); + } + private Properties compile(Class... types) throws IOException { + return process(types).getWrittenProperties(); + } + + private TestAutoConfigureAnnotationProcessor process(Class... types) { TestAutoConfigureAnnotationProcessor processor = new TestAutoConfigureAnnotationProcessor( this.compiler.getOutputLocation()); this.compiler.getTask(types).call(processor); - return processor.getWrittenProperties(); + return processor; } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureAnnotationProcessor.java b/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureAnnotationProcessor.java index b87627701ed..bfa249e1549 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureAnnotationProcessor.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureAnnotationProcessor.java @@ -60,7 +60,7 @@ public class TestAutoConfigureAnnotationProcessor extends AutoConfigureAnnotatio } public Properties getWrittenProperties() throws IOException { - File file = new File(this.outputLocation, PROPERTIES_PATH); + File file = getWrittenFile(); if (!file.exists()) { return null; } @@ -71,4 +71,8 @@ public class TestAutoConfigureAnnotationProcessor extends AutoConfigureAnnotatio } } + public File getWrittenFile() { + return new File(this.outputLocation, PROPERTIES_PATH); + } + }