Skip to content

Commit 08b16d5

Browse files
committed
Add hints for Availability targets
- Annotate ShellComponent with @Reflective and use custom AvailabilityReflectiveProcessor to find possible method targets returning Availability. - Backport #747 - Fixes #758
1 parent 8e98c18 commit 08b16d5

File tree

3 files changed

+114
-0
lines changed

3 files changed

+114
-0
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.shell;
17+
18+
import java.lang.reflect.AnnotatedElement;
19+
import java.lang.reflect.Method;
20+
21+
import org.springframework.aot.hint.ExecutableMode;
22+
import org.springframework.aot.hint.ReflectionHints;
23+
import org.springframework.aot.hint.annotation.ReflectiveProcessor;
24+
import org.springframework.util.ClassUtils;
25+
import org.springframework.util.ReflectionUtils;
26+
27+
/**
28+
* A {@link ReflectiveProcessor} implementation that registers methods of a
29+
* return type {@link Availability} from a target which is a class.
30+
*
31+
* @author Janne Valkealahti
32+
*/
33+
public final class AvailabilityReflectiveProcessor implements ReflectiveProcessor {
34+
35+
@Override
36+
public void registerReflectionHints(ReflectionHints hints, AnnotatedElement element) {
37+
if (element instanceof Class<?> type) {
38+
ReflectionUtils.doWithMethods(type, method -> {
39+
registerMethodHint(hints, method);
40+
}, mf -> {
41+
return ClassUtils.isAssignable(mf.getReturnType(), Availability.class)
42+
&& mf.getDeclaringClass() != Object.class;
43+
});
44+
}
45+
}
46+
47+
/**
48+
* Register {@link ReflectionHints} against the specified {@link Method}.
49+
*
50+
* @param hints the reflection hints instance to use
51+
* @param method the method to process
52+
*/
53+
protected void registerMethodHint(ReflectionHints hints, Method method) {
54+
hints.registerMethod(method, ExecutableMode.INVOKE);
55+
}
56+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.shell;
17+
18+
import org.junit.jupiter.api.Test;
19+
20+
import org.springframework.aot.hint.ExecutableMode;
21+
import org.springframework.aot.hint.ReflectionHints;
22+
import org.springframework.aot.hint.TypeReference;
23+
import org.springframework.aot.hint.annotation.Reflective;
24+
25+
import static org.assertj.core.api.Assertions.assertThat;
26+
27+
class AvailabilityReflectiveProcessorTests {
28+
29+
private final AvailabilityReflectiveProcessor processor = new AvailabilityReflectiveProcessor();
30+
31+
private final ReflectionHints hints = new ReflectionHints();
32+
33+
@Test
34+
void registerReflectiveHintsForMethod() throws NoSuchMethodException {
35+
processor.registerReflectionHints(hints, SampleBean.class);
36+
assertThat(hints.typeHints()).singleElement().satisfies(typeHint -> {
37+
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(SampleBean.class));
38+
assertThat(typeHint.getMemberCategories()).isEmpty();
39+
assertThat(typeHint.constructors()).isEmpty();
40+
assertThat(typeHint.fields()).isEmpty();
41+
assertThat(typeHint.methods()).singleElement().satisfies(methodHint -> {
42+
assertThat(methodHint.getName()).isEqualTo("test");
43+
assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE);
44+
});
45+
});
46+
}
47+
48+
@Reflective(AvailabilityReflectiveProcessor.class)
49+
static class SampleBean {
50+
51+
public Availability test() {
52+
return null;
53+
}
54+
}
55+
}

spring-shell-standard/src/main/java/org/springframework/shell/standard/ShellComponent.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import java.lang.annotation.RetentionPolicy;
2323
import java.lang.annotation.Target;
2424

25+
import org.springframework.aot.hint.annotation.Reflective;
26+
import org.springframework.shell.AvailabilityReflectiveProcessor;
2527
import org.springframework.stereotype.Component;
2628

2729
/**
@@ -38,6 +40,7 @@
3840
@Target(ElementType.TYPE)
3941
@Documented
4042
@Component
43+
@Reflective(AvailabilityReflectiveProcessor.class)
4144
public @interface ShellComponent {
4245

4346
/**

0 commit comments

Comments
 (0)