From 5e26d04c05bd051d858b96e3c03ad0b8dcdd8725 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 26 Jan 2018 18:18:53 +0100 Subject: [PATCH] Only generate time-to-live property for matching endpoints This commit makes sure that a "cache.time-to-live" property is not generated for endpoints that do not have a main read operation (i.e. a read operation with no parameter or only nullable parameters). This matches the endpoint feature that provides caching for only such operation. Closes gh-11703 --- .../appendix-application-properties.adoc | 2 - ...figurationMetadataAnnotationProcessor.java | 41 +++++++++++++++++-- ...ationMetadataAnnotationProcessorTests.java | 28 ++++++++++--- ...figurationMetadataAnnotationProcessor.java | 9 +++- .../configurationsample/ReadOperation.java | 38 +++++++++++++++++ .../endpoint/CustomPropertiesEndpoint.java | 4 +- .../endpoint/EnabledEndpoint.java | 12 +++++- .../endpoint/SimpleEndpoint.java | 8 +++- .../endpoint/SpecificEndpoint.java | 11 ++++- .../incremental/IncrementalEndpoint.java | 7 ++++ .../IncrementalSpecificEndpoint.java | 4 +- 11 files changed, 144 insertions(+), 20 deletions(-) create mode 100644 spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/ReadOperation.java diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 371fa43580..ecdae9e847 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -1241,11 +1241,9 @@ content into your application. Rather, pick only the properties that you need. management.endpoint.scheduledtasks.enabled= # Whether to enable the scheduled tasks endpoint. # SESSIONS ENDPOINT ({sc-spring-boot-actuator}/session/SessionsEndpoint.{sc-ext}[SessionsEndpoint]) - management.endpoint.sessions.cache.time-to-live=0ms # Maximum time that a response can be cached. management.endpoint.sessions.enabled= # Whether to enable the sessions endpoint. # SHUTDOWN ENDPOINT ({sc-spring-boot-actuator}/context/ShutdownEndpoint.{sc-ext}[ShutdownEndpoint]) - management.endpoint.shutdown.cache.time-to-live=0ms # Maximum time that a response can be cached. management.endpoint.shutdown.enabled=false # Whether to enable the shutdown endpoint. # THREAD DUMP ENDPOINT ({sc-spring-boot-actuator}/management/ThreadDumpEndpoint.{sc-ext}[ThreadDumpEndpoint]) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java index 7055069e43..e0aa48b5fa 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -46,6 +46,7 @@ import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.tools.Diagnostic.Kind; @@ -83,6 +84,11 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor static final String ENDPOINT_ANNOTATION = "org.springframework.boot.actuate." + "endpoint.annotation.Endpoint"; + static final String READ_OPERATION_ANNOTATION = "org.springframework.boot.actuate." + + "endpoint.annotation.ReadOperation"; + + static final String NULLABLE_ANNOTATION = "org.springframework.lang.Nullable"; + static final String LOMBOK_DATA_ANNOTATION = "lombok.Data"; static final String LOMBOK_GETTER_ANNOTATION = "lombok.Getter"; @@ -118,6 +124,10 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor return ENDPOINT_ANNOTATION; } + protected String readOperationAnnotation() { + return READ_OPERATION_ANNOTATION; + } + @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); @@ -425,9 +435,32 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor Boolean.class.getName(), type, null, String.format("Whether to enable the %s endpoint.", endpointId), (enabledByDefault == null ? true : enabledByDefault), null)); - this.metadataCollector.add(ItemMetadata.newProperty(endpointKey, - "cache.time-to-live", Duration.class.getName(), type, null, - "Maximum time that a response can be cached.", 0, null)); + if (hasMainReadOperation(element)) { + this.metadataCollector.add(ItemMetadata.newProperty(endpointKey, + "cache.time-to-live", Duration.class.getName(), type, null, + "Maximum time that a response can be cached.", 0, null)); + } + } + + private boolean hasMainReadOperation(TypeElement element) { + for (ExecutableElement method : ElementFilter + .methodsIn(element.getEnclosedElements())) { + if (hasAnnotation(method, readOperationAnnotation()) + && (TypeKind.VOID != method.getReturnType().getKind()) + && hasNoOrOptionalParameters(method)) { + return true; + } + } + return false; + } + + private boolean hasNoOrOptionalParameters(ExecutableElement method) { + for (VariableElement parameter : method.getParameters()) { + if (!hasAnnotation(parameter, NULLABLE_ANNOTATION)) { + return false; + } + } + return true; } private boolean isNested(Element returnType, VariableElement field, diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java index 78de970d92..a1622ebd8e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java @@ -591,8 +591,7 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).has(Metadata.withGroup("management.endpoint.disabled") .fromSource(DisabledEndpoint.class)); assertThat(metadata).has(enabledFlag("disabled", false)); - assertThat(metadata).has(cacheTtl("disabled")); - assertThat(metadata.getItems()).hasSize(3); + assertThat(metadata.getItems()).hasSize(2); } @Test @@ -601,8 +600,7 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).has(Metadata.withGroup("management.endpoint.enabled") .fromSource(EnabledEndpoint.class)); assertThat(metadata).has(enabledFlag("enabled", true)); - assertThat(metadata).has(cacheTtl("enabled")); - assertThat(metadata.getItems()).hasSize(3); + assertThat(metadata.getItems()).hasSize(2); } @Test @@ -634,8 +632,7 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata).has(Metadata.withGroup("management.endpoint.pascal-case") .fromSource(CamelCaseEndpoint.class)); assertThat(metadata).has(enabledFlag("PascalCase", "pascal-case", true)); - assertThat(metadata).has(cacheTtl("pascal-case")); - assertThat(metadata.getItems()).hasSize(3); + assertThat(metadata.getItems()).hasSize(2); } @Test @@ -658,6 +655,25 @@ public class ConfigurationMetadataAnnotationProcessorTests { assertThat(metadata.getItems()).hasSize(3); } + @Test + public void incrementalEndpointBuildChangeCacheFlag() throws Exception { + TestProject project = new TestProject(this.temporaryFolder, + IncrementalEndpoint.class); + ConfigurationMetadata metadata = project.fullBuild(); + assertThat(metadata).has(Metadata.withGroup("management.endpoint.incremental") + .fromSource(IncrementalEndpoint.class)); + assertThat(metadata).has(enabledFlag("incremental", true)); + assertThat(metadata).has(cacheTtl("incremental")); + assertThat(metadata.getItems()).hasSize(3); + project.replaceText(IncrementalEndpoint.class, "@Nullable String param", + "String param"); + metadata = project.incrementalBuild(IncrementalEndpoint.class); + assertThat(metadata).has(Metadata.withGroup("management.endpoint.incremental") + .fromSource(IncrementalEndpoint.class)); + assertThat(metadata).has(enabledFlag("incremental", true)); + assertThat(metadata.getItems()).hasSize(2); + } + @Test public void incrementalEndpointBuildEnableSpecificEndpoint() throws Exception { TestProject project = new TestProject(this.temporaryFolder, diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TestConfigurationMetadataAnnotationProcessor.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TestConfigurationMetadataAnnotationProcessor.java index 2e1cf80e14..b02abd1da5 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TestConfigurationMetadataAnnotationProcessor.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TestConfigurationMetadataAnnotationProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -48,6 +48,8 @@ public class TestConfigurationMetadataAnnotationProcessor static final String ENDPOINT_ANNOTATION = "org.springframework.boot.configurationsample.Endpoint"; + static final String READ_OPERATION_ANNOTATION = "org.springframework.boot.configurationsample.ReadOperation"; + private ConfigurationMetadata metadata; private final File outputLocation; @@ -76,6 +78,11 @@ public class TestConfigurationMetadataAnnotationProcessor return ENDPOINT_ANNOTATION; } + @Override + protected String readOperationAnnotation() { + return READ_OPERATION_ANNOTATION; + } + @Override protected ConfigurationMetadata writeMetaData() throws Exception { super.writeMetaData(); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/ReadOperation.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/ReadOperation.java new file mode 100644 index 0000000000..67576ef463 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/ReadOperation.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2018 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationsample; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Alternative to Spring Boot's {@code @ReadOperation} for testing (removes the need for a + * dependency on the real annotation). + * + * @author Stephane Nicoll + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ReadOperation { + + String[] produces() default {}; + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/CustomPropertiesEndpoint.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/CustomPropertiesEndpoint.java index 46553aace6..d5fa9ff559 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/CustomPropertiesEndpoint.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/CustomPropertiesEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -18,6 +18,7 @@ package org.springframework.boot.configurationsample.endpoint; import org.springframework.boot.configurationsample.ConfigurationProperties; import org.springframework.boot.configurationsample.Endpoint; +import org.springframework.boot.configurationsample.ReadOperation; /** * An endpoint with additional custom properties. @@ -30,6 +31,7 @@ public class CustomPropertiesEndpoint { private String name = "test"; + @ReadOperation public String getName() { return this.name; } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/EnabledEndpoint.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/EnabledEndpoint.java index d14e2f3313..2cbd24c905 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/EnabledEndpoint.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/EnabledEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -17,6 +17,7 @@ package org.springframework.boot.configurationsample.endpoint; import org.springframework.boot.configurationsample.Endpoint; +import org.springframework.boot.configurationsample.ReadOperation; /** * An endpoint that is enabled unless configured explicitly. @@ -26,4 +27,13 @@ import org.springframework.boot.configurationsample.Endpoint; @Endpoint(id = "enabled") public class EnabledEndpoint { + public String someMethod() { + return "not a read operation"; + } + + @ReadOperation + public String retrieve(String parameter, Integer anotherParameter) { + return "not a main read operation"; + } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/SimpleEndpoint.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/SimpleEndpoint.java index 3c4805c39a..a144b849a0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/SimpleEndpoint.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/SimpleEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -17,6 +17,7 @@ package org.springframework.boot.configurationsample.endpoint; import org.springframework.boot.configurationsample.Endpoint; +import org.springframework.boot.configurationsample.ReadOperation; /** * A simple endpoint with no default override. @@ -26,4 +27,9 @@ import org.springframework.boot.configurationsample.Endpoint; @Endpoint(id = "simple") public class SimpleEndpoint { + @ReadOperation + public String invoke() { + return "test"; + } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/SpecificEndpoint.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/SpecificEndpoint.java index bb319fd62a..b142bbb8cb 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/SpecificEndpoint.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/SpecificEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -17,14 +17,21 @@ package org.springframework.boot.configurationsample.endpoint; import org.springframework.boot.configurationsample.MetaEndpoint; +import org.springframework.boot.configurationsample.ReadOperation; +import org.springframework.lang.Nullable; /** * An meta-annotated endpoint similar to {@code @WebEndpoint} or {@code @JmxEndpoint} in - * Boot. + * Spring Boot. Also with a package private read operation that has an optional argument. * * @author Stephane Nicoll */ @MetaEndpoint(id = "specific", enableByDefault = true) public class SpecificEndpoint { + @ReadOperation + String invoke(@Nullable String param) { + return "test"; + } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalEndpoint.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalEndpoint.java index 817d4d85ad..df3fcf4f2b 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalEndpoint.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalEndpoint.java @@ -17,6 +17,8 @@ package org.springframework.boot.configurationsample.endpoint.incremental; import org.springframework.boot.configurationsample.Endpoint; +import org.springframework.boot.configurationsample.ReadOperation; +import org.springframework.lang.Nullable; /** * An endpoint that is enabled by default. @@ -26,4 +28,9 @@ import org.springframework.boot.configurationsample.Endpoint; @Endpoint(id = "incremental") public class IncrementalEndpoint { + @ReadOperation + public String invoke(@Nullable String param) { + return "test"; + } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalSpecificEndpoint.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalSpecificEndpoint.java index 02bf6338ae..368133db27 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalSpecificEndpoint.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/endpoint/incremental/IncrementalSpecificEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -20,7 +20,7 @@ import org.springframework.boot.configurationsample.MetaEndpoint; /** * An meta-annotated endpoint similar to {@code @WebEndpoint} or {@code @JmxEndpoint} in - * Boot. + * Spring Boot. * * @author Stephane Nicoll */