Commit cdf5f6e0 authored by Stephane Nicoll's avatar Stephane Nicoll

Add support for deprecation level in manual metadata

This commit allows to specify a deprecation level to a manual metadata
entry. The purpose of that new attribute is to distinguish cases where
the property is still bound (default) from cases where the property no
longer exists and won't be bound.

This gives the opportunity to IDEs to still show the property as an
error and offer documentation and an action to rename it if a
replacement exists.

Closes gh-9074
parent a22b3d95
...@@ -205,23 +205,30 @@ contain the following attributes: ...@@ -205,23 +205,30 @@ contain the following attributes:
|=== |===
|Name | Type |Purpose |Name | Type |Purpose
|`level`
|String
|The level of deprecation, can be either `warning` (default) or `error`. When a property
has a `warning` deprecation level it should still be bound in the environment. When it
has an `error` deprecation level however, the property is no longer managed and will not
be bound.
|`reason` |`reason`
| String |String
| A short description of the reason why the property was deprecated. May be omitted if no |A short description of the reason why the property was deprecated. May be omitted if no
reason is available. It is recommended that descriptions are a short paragraphs, reason is available. It is recommended that descriptions are a short paragraphs,
with the first line providing a concise summary. The last line in the description should with the first line providing a concise summary. The last line in the description should
end with a period (`.`). end with a period (`.`).
|`replacement` |`replacement`
| String |String
| The full name of the property that is _replacing_ this deprecated property. May be omitted |The full name of the property that is _replacing_ this deprecated property. May be omitted
if there is no replacement for this property. if there is no replacement for this property.
|=== |===
NOTE: Prior to Spring Boot 1.3, a single `deprecated` boolean attribute can be used instead of NOTE: Prior to Spring Boot 1.3, a single `deprecated` boolean attribute can be used
the `deprecation` element. This is still supported in a deprecated fashion and should no longer instead of the `deprecation` element. This is still supported in a deprecated fashion and
be used. If no reason and replacement are available, an empty `deprecation` object should be should no longer be used. If no reason and replacement are available, an empty
set. `deprecation` object should be set.
Deprecation can also be specified declaratively in code by adding the Deprecation can also be specified declaratively in code by adding the
`@DeprecatedConfigurationProperty` annotation to the getter exposing the deprecated `@DeprecatedConfigurationProperty` annotation to the getter exposing the deprecated
...@@ -252,10 +259,15 @@ was renamed to `app.foo.name` ...@@ -252,10 +259,15 @@ was renamed to `app.foo.name`
} }
---- ----
NOTE: There is no way to set a `level` as `warning` is always assumed since code is still
handling the property.
The code above makes sure that the deprecated property still works (delegating The code above makes sure that the deprecated property still works (delegating
to the `name` property behind the scenes). Once the `getTarget` and `setTarget` to the `name` property behind the scenes). Once the `getTarget` and `setTarget`
methods can be removed from your public API, the automatic deprecation hint in the methods can be removed from your public API, the automatic deprecation hint in the
meta-data will go away as well. meta-data will go away as well. If you want to keep a hint, adding manual meta-data with
an `error` deprecation level ensures that users are still informed about that property and
is particularly useful when a `replacement` is provided.
......
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -28,10 +28,24 @@ import java.io.Serializable; ...@@ -28,10 +28,24 @@ import java.io.Serializable;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class Deprecation implements Serializable { public class Deprecation implements Serializable {
private Level level = Level.WARNING;
private String reason; private String reason;
private String replacement; private String replacement;
/**
* Define the {@link Level} of deprecation.
* @return the deprecation level
*/
public Level getLevel() {
return this.level;
}
public void setLevel(Level level) {
this.level = level;
}
/** /**
* A reason why the related property is deprecated, if any. Can be multi-lines. * A reason why the related property is deprecated, if any. Can be multi-lines.
* @return the deprecation reason * @return the deprecation reason
...@@ -59,8 +73,25 @@ public class Deprecation implements Serializable { ...@@ -59,8 +73,25 @@ public class Deprecation implements Serializable {
@Override @Override
public String toString() { public String toString() {
return "Deprecation{" + "reason='" + this.reason + '\'' + ", replacement='" return "Deprecation{" + "level='" + this.level + '\'' + ", reason='"
+ this.replacement + '\'' + '}'; + this.reason + '\'' + ", replacement='" + this.replacement + '\'' + '}';
}
/**
* Define the deprecation level.
*/
public enum Level {
/**
* The property is still bound.
*/
WARNING,
/**
* The property has been removed and is no longer bound.
*/
ERROR
} }
} }
...@@ -170,6 +170,8 @@ class JsonReader { ...@@ -170,6 +170,8 @@ class JsonReader {
if (object.has("deprecation")) { if (object.has("deprecation")) {
JSONObject deprecationJsonObject = object.getJSONObject("deprecation"); JSONObject deprecationJsonObject = object.getJSONObject("deprecation");
Deprecation deprecation = new Deprecation(); Deprecation deprecation = new Deprecation();
deprecation.setLevel(parseDeprecationLevel(
deprecationJsonObject.optString("level", null)));
deprecation.setReason(deprecationJsonObject.optString("reason", null)); deprecation.setReason(deprecationJsonObject.optString("reason", null));
deprecation deprecation
.setReplacement(deprecationJsonObject.optString("replacement", null)); .setReplacement(deprecationJsonObject.optString("replacement", null));
...@@ -178,6 +180,18 @@ class JsonReader { ...@@ -178,6 +180,18 @@ class JsonReader {
return (object.optBoolean("deprecated") ? new Deprecation() : null); return (object.optBoolean("deprecated") ? new Deprecation() : null);
} }
private Deprecation.Level parseDeprecationLevel(String value) {
if (value != null) {
try {
return Deprecation.Level.valueOf(value.toUpperCase());
}
catch (IllegalArgumentException e) {
// let's use the default
}
}
return Deprecation.Level.WARNING;
}
private Object readItemValue(Object value) throws Exception { private Object readItemValue(Object value) throws Exception {
if (value instanceof JSONArray) { if (value instanceof JSONArray) {
JSONArray array = (JSONArray) value; JSONArray array = (JSONArray) value;
......
...@@ -148,7 +148,7 @@ public class JsonReaderTests extends AbstractConfigurationMetadataTests { ...@@ -148,7 +148,7 @@ public class JsonReaderTests extends AbstractConfigurationMetadataTests {
public void deprecatedMetadata() throws IOException { public void deprecatedMetadata() throws IOException {
RawConfigurationMetadata rawMetadata = readFor("deprecated"); RawConfigurationMetadata rawMetadata = readFor("deprecated");
List<ConfigurationMetadataItem> items = rawMetadata.getItems(); List<ConfigurationMetadataItem> items = rawMetadata.getItems();
assertThat(items).hasSize(3); assertThat(items).hasSize(5);
ConfigurationMetadataItem item = items.get(0); ConfigurationMetadataItem item = items.get(0);
assertProperty(item, "server.port", "server.port", Integer.class, null); assertProperty(item, "server.port", "server.port", Integer.class, null);
...@@ -157,19 +157,41 @@ public class JsonReaderTests extends AbstractConfigurationMetadataTests { ...@@ -157,19 +157,41 @@ public class JsonReaderTests extends AbstractConfigurationMetadataTests {
.isEqualTo("Server namespace has moved to spring.server"); .isEqualTo("Server namespace has moved to spring.server");
assertThat(item.getDeprecation().getReplacement()) assertThat(item.getDeprecation().getReplacement())
.isEqualTo("server.spring.port"); .isEqualTo("server.spring.port");
assertThat(item.getDeprecation().getLevel())
.isEqualTo(Deprecation.Level.WARNING);
ConfigurationMetadataItem item2 = items.get(1); ConfigurationMetadataItem item2 = items.get(1);
assertProperty(item2, "server.cluster-name", "server.cluster-name", String.class, assertProperty(item2, "server.cluster-name", "server.cluster-name", String.class,
null); null);
assertThat(item2.isDeprecated()).isTrue(); assertThat(item2.isDeprecated()).isTrue();
assertThat(item2.getDeprecation().getReason()).isEqualTo(null); assertThat(item2.getDeprecation().getReason()).isNull();
assertThat(item2.getDeprecation().getReplacement()).isEqualTo(null); assertThat(item2.getDeprecation().getReplacement()).isNull();
assertThat(item.getDeprecation().getLevel())
.isEqualTo(Deprecation.Level.WARNING);
ConfigurationMetadataItem item3 = items.get(2); ConfigurationMetadataItem item3 = items.get(2);
assertProperty(item3, "spring.server.name", "spring.server.name", String.class, assertProperty(item3, "spring.server.name", "spring.server.name", String.class,
null); null);
assertThat(item3.isDeprecated()).isFalse(); assertThat(item3.isDeprecated()).isFalse();
assertThat(item3.getDeprecation()).isEqualTo(null); assertThat(item3.getDeprecation()).isEqualTo(null);
ConfigurationMetadataItem item4 = items.get(3);
assertProperty(item4, "spring.server-name", "spring.server-name", String.class, null);
assertThat(item4.isDeprecated()).isTrue();
assertThat(item4.getDeprecation().getReason()).isNull();
assertThat(item4.getDeprecation().getReplacement())
.isEqualTo("spring.server.name");
assertThat(item4.getDeprecation().getLevel())
.isEqualTo(Deprecation.Level.ERROR);
ConfigurationMetadataItem item5 = items.get(4);
assertProperty(item5, "spring.server-name2", "spring.server-name2", String.class, null);
assertThat(item5.isDeprecated()).isTrue();
assertThat(item5.getDeprecation().getReason()).isNull();
assertThat(item5.getDeprecation().getReplacement())
.isEqualTo("spring.server.name");
assertThat(item5.getDeprecation().getLevel())
.isEqualTo(Deprecation.Level.WARNING);
} }
RawConfigurationMetadata readFor(String path) throws IOException { RawConfigurationMetadata readFor(String path) throws IOException {
......
...@@ -17,6 +17,22 @@ ...@@ -17,6 +17,22 @@
"name": "spring.server.name", "name": "spring.server.name",
"type": "java.lang.String", "type": "java.lang.String",
"deprecated": false "deprecated": false
} },
{
"name": "spring.server-name",
"type": "java.lang.String",
"deprecation": {
"level": "error",
"replacement": "spring.server.name"
}
},
{
"name": "spring.server-name2",
"type": "java.lang.String",
"deprecation": {
"level": "INVALID",
"replacement": "spring.server.name"
}
}
] ]
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment