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
pull/11811/head
Stephane Nicoll 7 years ago
parent 90545fb0c6
commit 5e26d04c05

@ -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. management.endpoint.scheduledtasks.enabled= # Whether to enable the scheduled tasks endpoint.
# SESSIONS ENDPOINT ({sc-spring-boot-actuator}/session/SessionsEndpoint.{sc-ext}[SessionsEndpoint]) # 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. management.endpoint.sessions.enabled= # Whether to enable the sessions endpoint.
# SHUTDOWN ENDPOINT ({sc-spring-boot-actuator}/context/ShutdownEndpoint.{sc-ext}[ShutdownEndpoint]) # 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. management.endpoint.shutdown.enabled=false # Whether to enable the shutdown endpoint.
# THREAD DUMP ENDPOINT ({sc-spring-boot-actuator}/management/ThreadDumpEndpoint.{sc-ext}[ThreadDumpEndpoint]) # THREAD DUMP ENDPOINT ({sc-spring-boot-actuator}/management/ThreadDumpEndpoint.{sc-ext}[ThreadDumpEndpoint])

@ -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"); * 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.
@ -46,6 +46,7 @@ import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType; import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements; import javax.lang.model.util.Elements;
import javax.tools.Diagnostic.Kind; import javax.tools.Diagnostic.Kind;
@ -83,6 +84,11 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
static final String ENDPOINT_ANNOTATION = "org.springframework.boot.actuate." static final String ENDPOINT_ANNOTATION = "org.springframework.boot.actuate."
+ "endpoint.annotation.Endpoint"; + "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_DATA_ANNOTATION = "lombok.Data";
static final String LOMBOK_GETTER_ANNOTATION = "lombok.Getter"; static final String LOMBOK_GETTER_ANNOTATION = "lombok.Getter";
@ -118,6 +124,10 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
return ENDPOINT_ANNOTATION; return ENDPOINT_ANNOTATION;
} }
protected String readOperationAnnotation() {
return READ_OPERATION_ANNOTATION;
}
@Override @Override
public SourceVersion getSupportedSourceVersion() { public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported(); return SourceVersion.latestSupported();
@ -425,9 +435,32 @@ public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor
Boolean.class.getName(), type, null, Boolean.class.getName(), type, null,
String.format("Whether to enable the %s endpoint.", endpointId), String.format("Whether to enable the %s endpoint.", endpointId),
(enabledByDefault == null ? true : enabledByDefault), null)); (enabledByDefault == null ? true : enabledByDefault), null));
this.metadataCollector.add(ItemMetadata.newProperty(endpointKey, if (hasMainReadOperation(element)) {
"cache.time-to-live", Duration.class.getName(), type, null, this.metadataCollector.add(ItemMetadata.newProperty(endpointKey,
"Maximum time that a response can be cached.", 0, null)); "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, private boolean isNested(Element returnType, VariableElement field,

@ -591,8 +591,7 @@ public class ConfigurationMetadataAnnotationProcessorTests {
assertThat(metadata).has(Metadata.withGroup("management.endpoint.disabled") assertThat(metadata).has(Metadata.withGroup("management.endpoint.disabled")
.fromSource(DisabledEndpoint.class)); .fromSource(DisabledEndpoint.class));
assertThat(metadata).has(enabledFlag("disabled", false)); assertThat(metadata).has(enabledFlag("disabled", false));
assertThat(metadata).has(cacheTtl("disabled")); assertThat(metadata.getItems()).hasSize(2);
assertThat(metadata.getItems()).hasSize(3);
} }
@Test @Test
@ -601,8 +600,7 @@ public class ConfigurationMetadataAnnotationProcessorTests {
assertThat(metadata).has(Metadata.withGroup("management.endpoint.enabled") assertThat(metadata).has(Metadata.withGroup("management.endpoint.enabled")
.fromSource(EnabledEndpoint.class)); .fromSource(EnabledEndpoint.class));
assertThat(metadata).has(enabledFlag("enabled", true)); assertThat(metadata).has(enabledFlag("enabled", true));
assertThat(metadata).has(cacheTtl("enabled")); assertThat(metadata.getItems()).hasSize(2);
assertThat(metadata.getItems()).hasSize(3);
} }
@Test @Test
@ -634,8 +632,7 @@ public class ConfigurationMetadataAnnotationProcessorTests {
assertThat(metadata).has(Metadata.withGroup("management.endpoint.pascal-case") assertThat(metadata).has(Metadata.withGroup("management.endpoint.pascal-case")
.fromSource(CamelCaseEndpoint.class)); .fromSource(CamelCaseEndpoint.class));
assertThat(metadata).has(enabledFlag("PascalCase", "pascal-case", true)); assertThat(metadata).has(enabledFlag("PascalCase", "pascal-case", true));
assertThat(metadata).has(cacheTtl("pascal-case")); assertThat(metadata.getItems()).hasSize(2);
assertThat(metadata.getItems()).hasSize(3);
} }
@Test @Test
@ -658,6 +655,25 @@ public class ConfigurationMetadataAnnotationProcessorTests {
assertThat(metadata.getItems()).hasSize(3); 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 @Test
public void incrementalEndpointBuildEnableSpecificEndpoint() throws Exception { public void incrementalEndpointBuildEnableSpecificEndpoint() throws Exception {
TestProject project = new TestProject(this.temporaryFolder, TestProject project = new TestProject(this.temporaryFolder,

@ -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"); * 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.
@ -48,6 +48,8 @@ public class TestConfigurationMetadataAnnotationProcessor
static final String ENDPOINT_ANNOTATION = "org.springframework.boot.configurationsample.Endpoint"; 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 ConfigurationMetadata metadata;
private final File outputLocation; private final File outputLocation;
@ -76,6 +78,11 @@ public class TestConfigurationMetadataAnnotationProcessor
return ENDPOINT_ANNOTATION; return ENDPOINT_ANNOTATION;
} }
@Override
protected String readOperationAnnotation() {
return READ_OPERATION_ANNOTATION;
}
@Override @Override
protected ConfigurationMetadata writeMetaData() throws Exception { protected ConfigurationMetadata writeMetaData() throws Exception {
super.writeMetaData(); super.writeMetaData();

@ -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 {};
}

@ -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"); * 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.
@ -18,6 +18,7 @@ package org.springframework.boot.configurationsample.endpoint;
import org.springframework.boot.configurationsample.ConfigurationProperties; import org.springframework.boot.configurationsample.ConfigurationProperties;
import org.springframework.boot.configurationsample.Endpoint; import org.springframework.boot.configurationsample.Endpoint;
import org.springframework.boot.configurationsample.ReadOperation;
/** /**
* An endpoint with additional custom properties. * An endpoint with additional custom properties.
@ -30,6 +31,7 @@ public class CustomPropertiesEndpoint {
private String name = "test"; private String name = "test";
@ReadOperation
public String getName() { public String getName() {
return this.name; return this.name;
} }

@ -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"); * 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.
@ -17,6 +17,7 @@
package org.springframework.boot.configurationsample.endpoint; package org.springframework.boot.configurationsample.endpoint;
import 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. * An endpoint that is enabled unless configured explicitly.
@ -26,4 +27,13 @@ import org.springframework.boot.configurationsample.Endpoint;
@Endpoint(id = "enabled") @Endpoint(id = "enabled")
public class EnabledEndpoint { 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";
}
} }

@ -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"); * 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.
@ -17,6 +17,7 @@
package org.springframework.boot.configurationsample.endpoint; package org.springframework.boot.configurationsample.endpoint;
import org.springframework.boot.configurationsample.Endpoint; import org.springframework.boot.configurationsample.Endpoint;
import org.springframework.boot.configurationsample.ReadOperation;
/** /**
* A simple endpoint with no default override. * A simple endpoint with no default override.
@ -26,4 +27,9 @@ import org.springframework.boot.configurationsample.Endpoint;
@Endpoint(id = "simple") @Endpoint(id = "simple")
public class SimpleEndpoint { public class SimpleEndpoint {
@ReadOperation
public String invoke() {
return "test";
}
} }

@ -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"); * 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.
@ -17,14 +17,21 @@
package org.springframework.boot.configurationsample.endpoint; package org.springframework.boot.configurationsample.endpoint;
import org.springframework.boot.configurationsample.MetaEndpoint; 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 * 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 * @author Stephane Nicoll
*/ */
@MetaEndpoint(id = "specific", enableByDefault = true) @MetaEndpoint(id = "specific", enableByDefault = true)
public class SpecificEndpoint { public class SpecificEndpoint {
@ReadOperation
String invoke(@Nullable String param) {
return "test";
}
} }

@ -17,6 +17,8 @@
package org.springframework.boot.configurationsample.endpoint.incremental; package org.springframework.boot.configurationsample.endpoint.incremental;
import org.springframework.boot.configurationsample.Endpoint; import org.springframework.boot.configurationsample.Endpoint;
import org.springframework.boot.configurationsample.ReadOperation;
import org.springframework.lang.Nullable;
/** /**
* An endpoint that is enabled by default. * An endpoint that is enabled by default.
@ -26,4 +28,9 @@ import org.springframework.boot.configurationsample.Endpoint;
@Endpoint(id = "incremental") @Endpoint(id = "incremental")
public class IncrementalEndpoint { public class IncrementalEndpoint {
@ReadOperation
public String invoke(@Nullable String param) {
return "test";
}
} }

@ -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"); * 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.
@ -20,7 +20,7 @@ import org.springframework.boot.configurationsample.MetaEndpoint;
/** /**
* An meta-annotated endpoint similar to {@code @WebEndpoint} or {@code @JmxEndpoint} in * An meta-annotated endpoint similar to {@code @WebEndpoint} or {@code @JmxEndpoint} in
* Boot. * Spring Boot.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */

Loading…
Cancel
Save