Support additional nullness signal for Actuator endpoints
This commit expands the detection of optional parameters for Actuator Endpoints. Before this commit, JSpecify's `@Nullable` annotation was not detected. See gh-46854 Signed-off-by: wonyongg <111210881+wonyongg@users.noreply.github.com>
This commit is contained in:
parent
088ef836c1
commit
75bcc2e118
|
@ -36,6 +36,7 @@ architectureCheck {
|
|||
|
||||
dependencies {
|
||||
testCompileOnly("com.google.code.findbugs:jsr305:3.0.2")
|
||||
testCompileOnly("org.jspecify:jspecify")
|
||||
|
||||
testImplementation(enforcedPlatform(project(":platform:spring-boot-dependencies")))
|
||||
testImplementation(project(":test-support:spring-boot-test-support"))
|
||||
|
|
|
@ -52,10 +52,13 @@ import org.springframework.boot.configurationprocessor.metadata.ItemDeprecation;
|
|||
* @author Stephane Nicoll
|
||||
* @author Scott Frederick
|
||||
* @author Moritz Halbritter
|
||||
* @author Wonyong Hwang
|
||||
*/
|
||||
class MetadataGenerationEnvironment {
|
||||
|
||||
private static final String NULLABLE_ANNOTATION = "org.springframework.lang.Nullable";
|
||||
private static final Set<String> NULLABLE_ANNOTATIONS = Set.of(
|
||||
"org.springframework.lang.Nullable",
|
||||
"org.jspecify.annotations.Nullable");
|
||||
|
||||
private static final Set<String> TYPE_EXCLUDES = Set.of("com.zaxxer.hikari.IConnectionCustomizer",
|
||||
"groovy.lang.MetaClass", "groovy.text.markup.MarkupTemplateEngine", "java.io.Writer", "java.io.PrintWriter",
|
||||
|
@ -265,6 +268,12 @@ class MetadataGenerationEnvironment {
|
|||
return annotation;
|
||||
}
|
||||
}
|
||||
|
||||
for (AnnotationMirror annotation : element.asType().getAnnotationMirrors()) {
|
||||
if (type.equals(annotation.getAnnotationType().toString())) {
|
||||
return annotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -368,7 +377,12 @@ class MetadataGenerationEnvironment {
|
|||
}
|
||||
|
||||
boolean hasNullableAnnotation(Element element) {
|
||||
return getAnnotation(element, NULLABLE_ANNOTATION) != null;
|
||||
for (String nullableAnnotation : NULLABLE_ANNOTATIONS) {
|
||||
if (getAnnotation(element, nullableAnnotation) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean hasOptionalParameterAnnotation(Element element) {
|
||||
|
|
|
@ -35,6 +35,8 @@ import org.springframework.boot.configurationsample.endpoint.SimpleEndpoint3;
|
|||
import org.springframework.boot.configurationsample.endpoint.SpecificEndpoint;
|
||||
import org.springframework.boot.configurationsample.endpoint.UnrestrictedAccessEndpoint;
|
||||
import org.springframework.boot.configurationsample.endpoint.incremental.IncrementalEndpoint;
|
||||
import org.springframework.boot.configurationsample.endpoint.NullableParameterEndpoint;
|
||||
import org.springframework.boot.configurationsample.endpoint.OptionalParameterEndpoint;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatRuntimeException;
|
||||
|
@ -45,6 +47,7 @@ import static org.assertj.core.api.Assertions.assertThatRuntimeException;
|
|||
* @author Stephane Nicoll
|
||||
* @author Scott Frederick
|
||||
* @author Moritz Halbritter
|
||||
* @author Wonyong Hwang
|
||||
*/
|
||||
class EndpointMetadataGenerationTests extends AbstractMetadataGenerationTests {
|
||||
|
||||
|
@ -192,6 +195,38 @@ class EndpointMetadataGenerationTests extends AbstractMetadataGenerationTests {
|
|||
"Existing property 'management.endpoint.simple.access' from type org.springframework.boot.configurationsample.endpoint.SimpleEndpoint has a conflicting value. Existing value: unrestricted, new value from type org.springframework.boot.configurationsample.endpoint.SimpleEndpoint3: none");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullableParameterEndpoint() {
|
||||
ConfigurationMetadata metadata = compile(NullableParameterEndpoint.class);
|
||||
assertThat(metadata).has(Metadata.withGroup("management.endpoint.nullable").fromSource(NullableParameterEndpoint.class));
|
||||
assertThat(metadata).has(access("nullable", Access.UNRESTRICTED));
|
||||
assertThat(metadata).has(cacheTtl("nullable"));
|
||||
assertThat(metadata.getItems()).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void optionalParameterEndpoint() {
|
||||
ConfigurationMetadata metadata = compile(OptionalParameterEndpoint.class);
|
||||
assertThat(metadata).has(Metadata.withGroup("management.endpoint.optional").fromSource(OptionalParameterEndpoint.class));
|
||||
assertThat(metadata).has(access("optional", Access.UNRESTRICTED));
|
||||
assertThat(metadata).has(cacheTtl("optional"));
|
||||
assertThat(metadata.getItems()).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullableAndOptionalParameterEquivalence() {
|
||||
ConfigurationMetadata nullableMetadata = compile(NullableParameterEndpoint.class);
|
||||
ConfigurationMetadata optionalMetadata = compile(OptionalParameterEndpoint.class);
|
||||
|
||||
assertThat(nullableMetadata.getItems()).hasSize(3);
|
||||
assertThat(optionalMetadata.getItems()).hasSize(3);
|
||||
|
||||
assertThat(nullableMetadata).has(access("nullable", Access.UNRESTRICTED));
|
||||
assertThat(optionalMetadata).has(access("optional", Access.UNRESTRICTED));
|
||||
assertThat(nullableMetadata).has(cacheTtl("nullable"));
|
||||
assertThat(optionalMetadata).has(cacheTtl("optional"));
|
||||
}
|
||||
|
||||
private Metadata.MetadataItemCondition access(String endpointId, Access defaultValue) {
|
||||
return defaultAccess(endpointId, endpointId, defaultValue);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.endpoint;
|
||||
|
||||
import org.springframework.boot.configurationsample.Endpoint;
|
||||
import org.springframework.boot.configurationsample.ReadOperation;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* An endpoint with @Nullable parameter to test.
|
||||
*
|
||||
* @author Wonyong Hwang
|
||||
*/
|
||||
@Endpoint(id = "nullable")
|
||||
public class NullableParameterEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public String invoke(@Nullable String parameter) {
|
||||
return "test with " + parameter;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2012-present 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
|
||||
*
|
||||
* https://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.endpoint;
|
||||
|
||||
import org.springframework.boot.configurationsample.Endpoint;
|
||||
import org.springframework.boot.configurationsample.ReadOperation;
|
||||
import org.springframework.boot.configurationsample.OptionalParameter;
|
||||
|
||||
/**
|
||||
* An endpoint with @OptionalParameter to compare with @Nullable behavior.
|
||||
*
|
||||
* @author Wonyong Hwang
|
||||
*/
|
||||
@Endpoint(id = "optional")
|
||||
public class OptionalParameterEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public String invoke(@OptionalParameter String parameter) {
|
||||
return "test with " + parameter;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue