From cdf5f6e0eecc8cdc97454a3e50a7c99b6b8f4b7f Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 11 May 2017 11:46:06 +0200 Subject: [PATCH] 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 --- .../appendix-configuration-metadata.adoc | 38 ++++++++++++------- .../configurationmetadata/Deprecation.java | 37 ++++++++++++++++-- .../configurationmetadata/JsonReader.java | 14 +++++++ .../JsonReaderTests.java | 28 ++++++++++++-- .../configuration-metadata-deprecated.json | 18 ++++++++- 5 files changed, 115 insertions(+), 20 deletions(-) diff --git a/spring-boot-docs/src/main/asciidoc/appendix-configuration-metadata.adoc b/spring-boot-docs/src/main/asciidoc/appendix-configuration-metadata.adoc index 43c12ed9cbf..464e82701d8 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-configuration-metadata.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-configuration-metadata.adoc @@ -205,23 +205,30 @@ contain the following attributes: |=== |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` -| String -| 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, - with the first line providing a concise summary. The last line in the description should - end with a period (`.`). +|String +|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, + with the first line providing a concise summary. The last line in the description should + end with a period (`.`). |`replacement` -| String -| The full name of the property that is _replacing_ this deprecated property. May be omitted - if there is no replacement for this property. +|String +|The full name of the property that is _replacing_ this deprecated property. May be omitted + 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 -the `deprecation` element. This is still supported in a deprecated fashion and should no longer -be used. If no reason and replacement are available, an empty `deprecation` object should be -set. +NOTE: Prior to Spring Boot 1.3, a single `deprecated` boolean attribute can be used +instead of the `deprecation` element. This is still supported in a deprecated fashion and +should no longer be used. If no reason and replacement are available, an empty +`deprecation` object should be set. Deprecation can also be specified declaratively in code by adding the `@DeprecatedConfigurationProperty` annotation to the getter exposing the deprecated @@ -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 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 -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. diff --git a/spring-boot-tools/spring-boot-configuration-metadata/src/main/java/org/springframework/boot/configurationmetadata/Deprecation.java b/spring-boot-tools/spring-boot-configuration-metadata/src/main/java/org/springframework/boot/configurationmetadata/Deprecation.java index 8261a2a85b4..061d7a4629d 100644 --- a/spring-boot-tools/spring-boot-configuration-metadata/src/main/java/org/springframework/boot/configurationmetadata/Deprecation.java +++ b/spring-boot-tools/spring-boot-configuration-metadata/src/main/java/org/springframework/boot/configurationmetadata/Deprecation.java @@ -1,5 +1,5 @@ /* - * 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"); * you may not use this file except in compliance with the License. @@ -28,10 +28,24 @@ import java.io.Serializable; @SuppressWarnings("serial") public class Deprecation implements Serializable { + private Level level = Level.WARNING; + private String reason; 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. * @return the deprecation reason @@ -59,8 +73,25 @@ public class Deprecation implements Serializable { @Override public String toString() { - return "Deprecation{" + "reason='" + this.reason + '\'' + ", replacement='" - + this.replacement + '\'' + '}'; + return "Deprecation{" + "level='" + this.level + '\'' + ", reason='" + + 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 + } } diff --git a/spring-boot-tools/spring-boot-configuration-metadata/src/main/java/org/springframework/boot/configurationmetadata/JsonReader.java b/spring-boot-tools/spring-boot-configuration-metadata/src/main/java/org/springframework/boot/configurationmetadata/JsonReader.java index 442df6ec353..732b3b9ba69 100644 --- a/spring-boot-tools/spring-boot-configuration-metadata/src/main/java/org/springframework/boot/configurationmetadata/JsonReader.java +++ b/spring-boot-tools/spring-boot-configuration-metadata/src/main/java/org/springframework/boot/configurationmetadata/JsonReader.java @@ -170,6 +170,8 @@ class JsonReader { if (object.has("deprecation")) { JSONObject deprecationJsonObject = object.getJSONObject("deprecation"); Deprecation deprecation = new Deprecation(); + deprecation.setLevel(parseDeprecationLevel( + deprecationJsonObject.optString("level", null))); deprecation.setReason(deprecationJsonObject.optString("reason", null)); deprecation .setReplacement(deprecationJsonObject.optString("replacement", null)); @@ -178,6 +180,18 @@ class JsonReader { 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 { if (value instanceof JSONArray) { JSONArray array = (JSONArray) value; diff --git a/spring-boot-tools/spring-boot-configuration-metadata/src/test/java/org/springframework/boot/configurationmetadata/JsonReaderTests.java b/spring-boot-tools/spring-boot-configuration-metadata/src/test/java/org/springframework/boot/configurationmetadata/JsonReaderTests.java index fce621433c1..567c341daa3 100644 --- a/spring-boot-tools/spring-boot-configuration-metadata/src/test/java/org/springframework/boot/configurationmetadata/JsonReaderTests.java +++ b/spring-boot-tools/spring-boot-configuration-metadata/src/test/java/org/springframework/boot/configurationmetadata/JsonReaderTests.java @@ -148,7 +148,7 @@ public class JsonReaderTests extends AbstractConfigurationMetadataTests { public void deprecatedMetadata() throws IOException { RawConfigurationMetadata rawMetadata = readFor("deprecated"); List items = rawMetadata.getItems(); - assertThat(items).hasSize(3); + assertThat(items).hasSize(5); ConfigurationMetadataItem item = items.get(0); assertProperty(item, "server.port", "server.port", Integer.class, null); @@ -157,19 +157,41 @@ public class JsonReaderTests extends AbstractConfigurationMetadataTests { .isEqualTo("Server namespace has moved to spring.server"); assertThat(item.getDeprecation().getReplacement()) .isEqualTo("server.spring.port"); + assertThat(item.getDeprecation().getLevel()) + .isEqualTo(Deprecation.Level.WARNING); ConfigurationMetadataItem item2 = items.get(1); assertProperty(item2, "server.cluster-name", "server.cluster-name", String.class, null); assertThat(item2.isDeprecated()).isTrue(); - assertThat(item2.getDeprecation().getReason()).isEqualTo(null); - assertThat(item2.getDeprecation().getReplacement()).isEqualTo(null); + assertThat(item2.getDeprecation().getReason()).isNull(); + assertThat(item2.getDeprecation().getReplacement()).isNull(); + assertThat(item.getDeprecation().getLevel()) + .isEqualTo(Deprecation.Level.WARNING); ConfigurationMetadataItem item3 = items.get(2); assertProperty(item3, "spring.server.name", "spring.server.name", String.class, null); assertThat(item3.isDeprecated()).isFalse(); 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 { diff --git a/spring-boot-tools/spring-boot-configuration-metadata/src/test/resources/metadata/configuration-metadata-deprecated.json b/spring-boot-tools/spring-boot-configuration-metadata/src/test/resources/metadata/configuration-metadata-deprecated.json index 1a2cdec82ab..af288967e19 100644 --- a/spring-boot-tools/spring-boot-configuration-metadata/src/test/resources/metadata/configuration-metadata-deprecated.json +++ b/spring-boot-tools/spring-boot-configuration-metadata/src/test/resources/metadata/configuration-metadata-deprecated.json @@ -17,6 +17,22 @@ "name": "spring.server.name", "type": "java.lang.String", "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