Skip to content

Commit bc6a92a

Browse files
committed
Include deprecated configuration properties in reference documentation
Add a new appendix section to show deprecated properties and when possible their replacement or reason. Closes gh-47622
1 parent 7647029 commit bc6a92a

File tree

12 files changed

+230
-37
lines changed

12 files changed

+230
-37
lines changed

buildSrc/src/main/java/org/springframework/boot/build/context/properties/CompoundRow.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ void addProperty(ConfigurationProperty property) {
4242
this.propertyNames.add(property.getDisplayName());
4343
}
4444

45+
boolean isEmpty() {
46+
return this.propertyNames.isEmpty();
47+
}
48+
4549
@Override
4650
void write(Asciidoc asciidoc) {
4751
asciidoc.append("|");

buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationProperty.java

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
* A configuration property.
2323
*
2424
* @author Andy Wilkinson
25+
* @author Phillip Webb
2526
*/
2627
class ConfigurationProperty {
2728

@@ -35,16 +36,20 @@ class ConfigurationProperty {
3536

3637
private final boolean deprecated;
3738

39+
private final Deprecation deprecation;
40+
3841
ConfigurationProperty(String name, String type) {
39-
this(name, type, null, null, false);
42+
this(name, type, null, null, false, null);
4043
}
4144

42-
ConfigurationProperty(String name, String type, Object defaultValue, String description, boolean deprecated) {
45+
ConfigurationProperty(String name, String type, Object defaultValue, String description, boolean deprecated,
46+
Deprecation deprecation) {
4347
this.name = name;
4448
this.type = type;
4549
this.defaultValue = defaultValue;
4650
this.description = description;
4751
this.deprecated = deprecated;
52+
this.deprecation = deprecation;
4853
}
4954

5055
String getName() {
@@ -71,18 +76,39 @@ boolean isDeprecated() {
7176
return this.deprecated;
7277
}
7378

79+
Deprecation getDeprecation() {
80+
return this.deprecation;
81+
}
82+
7483
@Override
7584
public String toString() {
7685
return "ConfigurationProperty [name=" + this.name + ", type=" + this.type + "]";
7786
}
7887

88+
@SuppressWarnings("unchecked")
7989
static ConfigurationProperty fromJsonProperties(Map<String, Object> property) {
8090
String name = (String) property.get("name");
8191
String type = (String) property.get("type");
8292
Object defaultValue = property.get("defaultValue");
8393
String description = (String) property.get("description");
8494
boolean deprecated = property.containsKey("deprecated");
85-
return new ConfigurationProperty(name, type, defaultValue, description, deprecated);
95+
Map<String, Object> deprecation = (Map<String, Object>) property.get("deprecation");
96+
return new ConfigurationProperty(name, type, defaultValue, description, deprecated,
97+
Deprecation.fromJsonProperties(deprecation));
98+
}
99+
100+
record Deprecation(String reason, String replacement, String since) {
101+
102+
static Deprecation fromJsonProperties(Map<String, Object> property) {
103+
if (property == null) {
104+
return null;
105+
}
106+
String reason = (String) property.get("reason");
107+
String replacement = (String) property.get("replacement");
108+
String since = (String) property.get("since");
109+
return new Deprecation(reason, replacement, since);
110+
}
111+
86112
}
87113

88114
}

buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentConfigurationProperties.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import org.gradle.api.Task;
2323
import org.gradle.api.file.DirectoryProperty;
2424
import org.gradle.api.file.FileCollection;
25+
import org.gradle.api.provider.Property;
26+
import org.gradle.api.tasks.Input;
2527
import org.gradle.api.tasks.InputFiles;
2628
import org.gradle.api.tasks.OutputDirectory;
2729
import org.gradle.api.tasks.PathSensitive;
@@ -50,12 +52,15 @@ public void setConfigurationPropertyMetadata(FileCollection configurationPropert
5052
this.configurationPropertyMetadata = configurationPropertyMetadata;
5153
}
5254

55+
@Input
56+
public abstract Property<Boolean> getDeprecated();
57+
5358
@OutputDirectory
5459
public abstract DirectoryProperty getOutputDir();
5560

5661
@TaskAction
5762
void documentConfigurationProperties() throws IOException {
58-
Snippets snippets = new Snippets(this.configurationPropertyMetadata);
63+
Snippets snippets = new Snippets(this.configurationPropertyMetadata, getDeprecated().getOrElse(false));
5964
snippets.add("application-properties.core", "Core Properties", this::corePrefixes);
6065
snippets.add("application-properties.cache", "Cache Properties", this::cachePrefixes);
6166
snippets.add("application-properties.mail", "Mail Properties", this::mailPrefixes);

buildSrc/src/main/java/org/springframework/boot/build/context/properties/SingleRow.java

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.util.Arrays;
2020
import java.util.stream.Collectors;
2121

22+
import org.springframework.boot.build.context.properties.ConfigurationProperty.Deprecation;
23+
2224
/**
2325
* Table row containing a single configuration property.
2426
*
@@ -28,17 +30,21 @@
2830
*/
2931
class SingleRow extends Row {
3032

31-
private final String displayName;
32-
33-
private final String description;
33+
private final Snippets snippets;
3434

3535
private final String defaultValue;
3636

37+
private final ConfigurationProperty property;
38+
3739
SingleRow(Snippet snippet, ConfigurationProperty property) {
40+
this(null, snippet, property);
41+
}
42+
43+
SingleRow(Snippets snippets, Snippet snippet, ConfigurationProperty property) {
3844
super(snippet, property.getName());
39-
this.displayName = property.getDisplayName();
40-
this.description = property.getDescription();
45+
this.snippets = snippets;
4146
this.defaultValue = getDefaultValue(property.getDefaultValue());
47+
this.property = property;
4248
}
4349

4450
private String getDefaultValue(Object defaultValue) {
@@ -57,19 +63,41 @@ private String getDefaultValue(Object defaultValue) {
5763
void write(Asciidoc asciidoc) {
5864
asciidoc.append("|");
5965
asciidoc.append("[[" + getAnchor() + "]]");
60-
asciidoc.appendln("xref:#" + getAnchor() + "[`+", this.displayName, "+`]");
66+
asciidoc.appendln("xref:#" + getAnchor() + "[`+", this.property.getDisplayName(), "+`]");
6167
writeDescription(asciidoc);
6268
writeDefaultValue(asciidoc);
6369
}
6470

6571
private void writeDescription(Asciidoc builder) {
66-
if (this.description == null || this.description.isEmpty()) {
67-
builder.appendln("|");
72+
builder.append("|");
73+
if (this.property.isDeprecated()) {
74+
Deprecation deprecation = this.property.getDeprecation();
75+
String replacement = (deprecation != null) ? deprecation.replacement() : null;
76+
String reason = (deprecation != null) ? deprecation.reason() : null;
77+
if (replacement != null && !replacement.isEmpty()) {
78+
String xref = (this.snippets != null) ? this.snippets.findXref(deprecation.replacement()) : null;
79+
if (xref != null) {
80+
builder.append("Replaced by xref:" + xref + "[`+" + deprecation.replacement() + "+`]");
81+
}
82+
else {
83+
builder.append("Replaced by `+" + deprecation.replacement() + "+`");
84+
}
85+
}
86+
else if (reason != null && !reason.isEmpty()) {
87+
builder.append("+++", clean(reason), "+++");
88+
}
6889
}
6990
else {
70-
String cleanedDescription = this.description.replace("|", "\\|").replace("<", "&lt;").replace(">", "&gt;");
71-
builder.appendln("|+++", cleanedDescription, "+++");
91+
String description = this.property.getDescription();
92+
if (description != null && !description.isEmpty()) {
93+
builder.append("+++", clean(description), "+++");
94+
}
7295
}
96+
builder.appendln();
97+
}
98+
99+
private String clean(String text) {
100+
return text.replace("|", "\\|").replace("<", "&lt;").replace(">", "&gt;");
73101
}
74102

75103
private void writeDefaultValue(Asciidoc builder) {

buildSrc/src/main/java/org/springframework/boot/build/context/properties/Snippet.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ String getTitle() {
7171
return this.title;
7272
}
7373

74+
Set<String> getPrefixes() {
75+
return this.prefixes;
76+
}
77+
78+
Map<String, String> getOverrides() {
79+
return this.overrides;
80+
}
81+
7482
void forEachPrefix(Consumer<String> action) {
7583
this.prefixes.forEach(action);
7684
}

buildSrc/src/main/java/org/springframework/boot/build/context/properties/Snippets.java

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@ class Snippets {
4242

4343
private final List<Snippet> snippets = new ArrayList<>();
4444

45-
Snippets(FileCollection configurationPropertyMetadata) {
45+
private final boolean deprecated;
46+
47+
Snippets(FileCollection configurationPropertyMetadata, boolean deprecated) {
4648
this.properties = ConfigurationProperties.fromFiles(configurationPropertyMetadata);
49+
this.deprecated = deprecated;
4750
}
4851

4952
void add(String anchor, String title, Consumer<Snippet.Config> config) {
@@ -53,14 +56,14 @@ void add(String anchor, String title, Consumer<Snippet.Config> config) {
5356
void writeTo(Path outputDirectory) throws IOException {
5457
createDirectory(outputDirectory);
5558
Set<String> remaining = this.properties.stream()
56-
.filter((property) -> !property.isDeprecated())
59+
.filter(this::shouldAdd)
5760
.map(ConfigurationProperty::getName)
5861
.collect(Collectors.toSet());
5962
for (Snippet snippet : this.snippets) {
6063
Set<String> written = writeSnippet(outputDirectory, snippet, remaining);
6164
remaining.removeAll(written);
6265
}
63-
if (!remaining.isEmpty()) {
66+
if (!this.deprecated && !remaining.isEmpty()) {
6467
throw new IllegalStateException(
6568
"The following keys were not written to the documentation: " + String.join(", ", remaining));
6669
}
@@ -70,18 +73,22 @@ private Set<String> writeSnippet(Path outputDirectory, Snippet snippet, Set<Stri
7073
Table table = new Table();
7174
Set<String> added = new HashSet<>();
7275
snippet.forEachOverride((prefix, description) -> {
73-
CompoundRow row = new CompoundRow(snippet, prefix, description);
76+
CompoundRow row = new CompoundRow(snippet, prefix, (!this.deprecated) ? description : "");
7477
remaining.stream().filter((candidate) -> candidate.startsWith(prefix)).forEach((name) -> {
75-
if (added.add(name)) {
76-
row.addProperty(this.properties.get(name));
78+
ConfigurationProperty property = this.properties.get(name);
79+
if (shouldAdd(property) && added.add(name)) {
80+
row.addProperty(property);
7781
}
7882
});
79-
table.addRow(row);
83+
if (!row.isEmpty()) {
84+
table.addRow(row);
85+
}
8086
});
8187
snippet.forEachPrefix((prefix) -> {
8288
remaining.stream().filter((candidate) -> candidate.startsWith(prefix)).forEach((name) -> {
83-
if (added.add(name)) {
84-
table.addRow(new SingleRow(snippet, this.properties.get(name)));
89+
ConfigurationProperty property = this.properties.get(name);
90+
if (shouldAdd(property) && added.add(name)) {
91+
table.addRow(new SingleRow(this, snippet, property));
8592
}
8693
});
8794
});
@@ -90,13 +97,39 @@ private Set<String> writeSnippet(Path outputDirectory, Snippet snippet, Set<Stri
9097
return added;
9198
}
9299

100+
String findXref(String name) {
101+
ConfigurationProperty property = this.properties.get(name);
102+
if (property == null || property.isDeprecated()) {
103+
return null;
104+
}
105+
for (Snippet snippet : this.snippets) {
106+
for (String prefix : snippet.getOverrides().keySet()) {
107+
if (name.startsWith(prefix)) {
108+
return null;
109+
}
110+
}
111+
for (String prefix : snippet.getPrefixes()) {
112+
if (name.startsWith(prefix)) {
113+
return "appendix:application-properties/index.adoc#%s.%s".formatted(snippet.getAnchor(), name);
114+
}
115+
}
116+
}
117+
return null;
118+
}
119+
120+
private boolean shouldAdd(ConfigurationProperty property) {
121+
return (property == null || property.isDeprecated() == this.deprecated);
122+
}
123+
93124
private Asciidoc getAsciidoc(Snippet snippet, Table table) {
94125
Asciidoc asciidoc = new Asciidoc();
95-
// We have to prepend 'appendix.' as a section id here, otherwise the
96-
// spring-asciidoctor-extensions:section-id asciidoctor extension complains
97-
asciidoc.appendln("[[appendix." + snippet.getAnchor() + "]]");
98-
asciidoc.appendln("== ", snippet.getTitle());
99-
table.write(asciidoc);
126+
if (!table.isEmpty()) {
127+
// We have to prepend 'appendix.' as a section id here, otherwise the
128+
// spring-asciidoctor-extensions:section-id asciidoctor extension complains
129+
asciidoc.appendln("[[appendix." + ((this.deprecated) ? "deprecated-" : "") + snippet.getAnchor() + "]]");
130+
asciidoc.appendln("== ", ((this.deprecated) ? "Deprecated " : "") + snippet.getTitle());
131+
table.write(asciidoc);
132+
}
100133
return asciidoc;
101134
}
102135

buildSrc/src/main/java/org/springframework/boot/build/context/properties/Table.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,8 @@ void write(Asciidoc asciidoc) {
4444
asciidoc.appendln("|===");
4545
}
4646

47+
boolean isEmpty() {
48+
return this.rows.isEmpty();
49+
}
50+
4751
}

buildSrc/src/test/java/org/springframework/boot/build/context/properties/SingleRowTests.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class SingleRowTests {
3535
@Test
3636
void simpleProperty() {
3737
ConfigurationProperty property = new ConfigurationProperty("spring.test.prop", "java.lang.String", "something",
38-
"This is a description.", false);
38+
"This is a description.", false, null);
3939
SingleRow row = new SingleRow(SNIPPET, property);
4040
Asciidoc asciidoc = new Asciidoc();
4141
row.write(asciidoc);
@@ -46,7 +46,7 @@ void simpleProperty() {
4646
@Test
4747
void noDefaultValue() {
4848
ConfigurationProperty property = new ConfigurationProperty("spring.test.prop", "java.lang.String", null,
49-
"This is a description.", false);
49+
"This is a description.", false, null);
5050
SingleRow row = new SingleRow(SNIPPET, property);
5151
Asciidoc asciidoc = new Asciidoc();
5252
row.write(asciidoc);
@@ -57,7 +57,7 @@ void noDefaultValue() {
5757
@Test
5858
void defaultValueWithPipes() {
5959
ConfigurationProperty property = new ConfigurationProperty("spring.test.prop", "java.lang.String",
60-
"first|second", "This is a description.", false);
60+
"first|second", "This is a description.", false, null);
6161
SingleRow row = new SingleRow(SNIPPET, property);
6262
Asciidoc asciidoc = new Asciidoc();
6363
row.write(asciidoc);
@@ -68,7 +68,7 @@ void defaultValueWithPipes() {
6868
@Test
6969
void defaultValueWithBackslash() {
7070
ConfigurationProperty property = new ConfigurationProperty("spring.test.prop", "java.lang.String",
71-
"first\\second", "This is a description.", false);
71+
"first\\second", "This is a description.", false, null);
7272
SingleRow row = new SingleRow(SNIPPET, property);
7373
Asciidoc asciidoc = new Asciidoc();
7474
row.write(asciidoc);
@@ -79,7 +79,7 @@ void defaultValueWithBackslash() {
7979
@Test
8080
void descriptionWithPipe() {
8181
ConfigurationProperty property = new ConfigurationProperty("spring.test.prop", "java.lang.String", null,
82-
"This is a description with a | pipe.", false);
82+
"This is a description with a | pipe.", false, null);
8383
SingleRow row = new SingleRow(SNIPPET, property);
8484
Asciidoc asciidoc = new Asciidoc();
8585
row.write(asciidoc);
@@ -90,7 +90,7 @@ void descriptionWithPipe() {
9090
@Test
9191
void mapProperty() {
9292
ConfigurationProperty property = new ConfigurationProperty("spring.test.prop",
93-
"java.util.Map<java.lang.String,java.lang.String>", null, "This is a description.", false);
93+
"java.util.Map<java.lang.String,java.lang.String>", null, "This is a description.", false, null);
9494
SingleRow row = new SingleRow(SNIPPET, property);
9595
Asciidoc asciidoc = new Asciidoc();
9696
row.write(asciidoc);
@@ -102,7 +102,7 @@ void mapProperty() {
102102
void listProperty() {
103103
String[] defaultValue = new String[] { "first", "second", "third" };
104104
ConfigurationProperty property = new ConfigurationProperty("spring.test.prop",
105-
"java.util.List<java.lang.String>", defaultValue, "This is a description.", false);
105+
"java.util.List<java.lang.String>", defaultValue, "This is a description.", false, null);
106106
SingleRow row = new SingleRow(SNIPPET, property);
107107
Asciidoc asciidoc = new Asciidoc();
108108
row.write(asciidoc);

buildSrc/src/test/java/org/springframework/boot/build/context/properties/TableTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ class TableTests {
3636
void simpleTable() {
3737
Table table = new Table();
3838
table.addRow(new SingleRow(SNIPPET, new ConfigurationProperty("spring.test.prop", "java.lang.String",
39-
"something", "This is a description.", false)));
39+
"something", "This is a description.", false, null)));
4040
table.addRow(new SingleRow(SNIPPET, new ConfigurationProperty("spring.test.other", "java.lang.String",
41-
"other value", "This is another description.", false)));
41+
"other value", "This is another description.", false, null)));
4242
Asciidoc asciidoc = new Asciidoc();
4343
table.write(asciidoc);
4444
// @formatter:off

0 commit comments

Comments
 (0)