Skip to content

Commit de1a3ba

Browse files
committed
New annotation model
- This is a first commit to add new annotation model which eventually will replace old legacy annotations like ShellComponent, ShellMethod, @ShellOption, etc. - Adds subset of features needed for parity with manual use of CommandRegistration. - Relates #637 - Relates #638 - Relates #639 - Relates #640 - Relates #641
1 parent d1c482c commit de1a3ba

22 files changed

+1781
-15
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
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.command.annotation;
17+
18+
import java.lang.annotation.Documented;
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Retention;
21+
import java.lang.annotation.RetentionPolicy;
22+
import java.lang.annotation.Target;
23+
24+
import org.springframework.shell.context.InteractionMode;
25+
26+
/**
27+
* Annotation marking a method to be a candicate for a shell command target.
28+
*
29+
* @author Janne Valkealahti
30+
*/
31+
@Retention(RetentionPolicy.RUNTIME)
32+
@Target({ ElementType.TYPE, ElementType.METHOD })
33+
@Documented
34+
public @interface Command {
35+
36+
/**
37+
* Define command as an array. Given that command should be
38+
* {@code command1 sub1} it can be defined as:
39+
*
40+
* <pre class="code">
41+
* command = { "command1", "sub1" }
42+
* command = "command1 sub1"
43+
* </pre>
44+
*
45+
* Values are split and trimmed meaning spaces doesn't matter.
46+
*
47+
* <p>
48+
* <b>Supported at the type level as well as at the method level!</b>
49+
* When used at the type level, all method-level mappings inherit this primary
50+
* command to use it as a prefix.
51+
*
52+
* <pre class="code">
53+
* &#64;Command(command = "command1")
54+
* class MyCommands {
55+
*
56+
* &#64;Command(command = "sub1")
57+
* void sub1(){}
58+
* }
59+
* </pre>
60+
*
61+
* @return the command as an array
62+
*/
63+
String[] command() default {};
64+
65+
/**
66+
* Define alias as an array. Given that alias should be
67+
* {@code alias1 sub1} it can be defined as:
68+
*
69+
* <pre class="code">
70+
* command = { "alias1", "sub1" }
71+
* command = "alias1 sub1"
72+
* </pre>
73+
*
74+
* Values are split and trimmed meaning spaces doesn't matter.
75+
*
76+
* <p>
77+
* <b>Supported at the type level as well as at the method level!</b>
78+
* When used at the type level, all method-level mappings inherit this primary
79+
* alias to use it as a prefix.
80+
*
81+
* <pre class="code">
82+
* &#64;Command(alias = "alias1")
83+
* class MyCommands {
84+
*
85+
* &#64;Command(alias = "sub1")
86+
* void sub1(){}
87+
* }
88+
* </pre>
89+
*
90+
* @return the aliases as an array
91+
*/
92+
String[] alias() default {};
93+
94+
/**
95+
* Define a command group.
96+
*
97+
* <p>
98+
* <b>Supported at the type level as well as at the method level!</b>
99+
* When used at the type level, all method-level group inherit this primary
100+
* group. Can be overridden on method-level.
101+
*
102+
* @return the command group
103+
*/
104+
String group() default "";
105+
106+
/**
107+
* Define a command description.
108+
*
109+
* <p>
110+
* <b>Supported at the type level as well as at the method level!</b>
111+
* When used at the type level, all method-level descriptions inherit this primary
112+
* field. Can be overridden on method-level.
113+
*
114+
* @return the command description
115+
*/
116+
String description() default "";
117+
118+
/**
119+
* Define command to be hidden.
120+
*
121+
* <p>
122+
* <b>Supported at the type level as well as at the method level!</b>
123+
* When used at the type level, all method-level mappings inherit this primary
124+
* hidden field.
125+
*
126+
* <pre class="code">
127+
* &#64;Command(hidden = true)
128+
* class MyCommands {
129+
*
130+
* &#64;Command
131+
* void sub1(){
132+
* // sub1 command is hidden
133+
* }
134+
* }
135+
* </pre>
136+
*
137+
* @return true if command should be hidden
138+
*/
139+
boolean hidden() default false;
140+
141+
/**
142+
* Define interaction mode for a command as a hint when command should be
143+
* available. For example presense of some commands doesn't make sense if shell
144+
* is running as non-interactive mode and vice versa.
145+
*
146+
* <p>
147+
* <b>Supported at the type level as well as at the method level!</b>
148+
* When used at the type level, all method-level mappings inherit this primary
149+
* field.
150+
*
151+
* Type is an array to be able to indicate that default don't have anyting defined.
152+
*
153+
* @return interaction modes
154+
*/
155+
InteractionMode[] interactionMode() default {};
156+
157+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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.command.annotation;
17+
18+
import java.lang.annotation.Documented;
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Retention;
21+
import java.lang.annotation.RetentionPolicy;
22+
import java.lang.annotation.Target;
23+
24+
import org.springframework.context.annotation.Import;
25+
import org.springframework.core.annotation.AliasFor;
26+
import org.springframework.shell.command.annotation.support.CommandScanRegistrar;
27+
28+
/**
29+
* Configures the base packages used when scanning for {@link Command @Comamnd}
30+
* classes. One of {@link #basePackageClasses()}, {@link #basePackages()} or its
31+
* alias {@link #value()} may be specified to define specific packages to scan.
32+
* If specific packages are not defined scanning will occur from the package of
33+
* the class with this annotation.
34+
*
35+
* @author Janne Valkealahti
36+
*/
37+
@Target(ElementType.TYPE)
38+
@Retention(RetentionPolicy.RUNTIME)
39+
@Documented
40+
@Import(CommandScanRegistrar.class)
41+
@EnableCommand
42+
public @interface CommandScan {
43+
44+
/**
45+
* Alias for the {@link #basePackages()} attribute. Allows for more concise
46+
* annotation declarations e.g.: {@code @CommandScan("org.my.pkg")} instead of
47+
* {@code @CommandScan(basePackages="org.my.pkg")}.
48+
*
49+
* @return the base packages to scan
50+
*/
51+
@AliasFor("basePackages")
52+
String[] value() default {};
53+
54+
/**
55+
* Base packages to scan for commands. {@link #value()} is an alias for (and
56+
* mutually exclusive with) this attribute.
57+
* <p>
58+
* Use {@link #basePackageClasses()} for a type-safe alternative to String-based
59+
* package names.
60+
*
61+
* @return the base packages to scan
62+
*/
63+
@AliasFor("value")
64+
String[] basePackages() default {};
65+
66+
/**
67+
* Type-safe alternative to {@link #basePackages()} for specifying the packages
68+
* to scan for commands. The package of each class specified will be scanned.
69+
* <p>
70+
* Consider creating a special no-op marker class or interface in each package
71+
* that serves no purpose other than being referenced by this attribute.
72+
*
73+
* @return classes from the base packages to scan
74+
*/
75+
Class<?>[] basePackageClasses() default {};
76+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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.command.annotation;
17+
18+
import java.lang.annotation.Documented;
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Retention;
21+
import java.lang.annotation.RetentionPolicy;
22+
import java.lang.annotation.Target;
23+
24+
import org.springframework.context.annotation.Import;
25+
import org.springframework.shell.command.annotation.support.EnableCommandRegistrar;
26+
27+
/**
28+
* Enable support for {@link Command @Command} annotated classes.
29+
* {@code @Command} classes can be registered directly on this annotation.
30+
*
31+
* @author Janne Valkealahti
32+
*/
33+
@Target(ElementType.TYPE)
34+
@Retention(RetentionPolicy.RUNTIME)
35+
@Documented
36+
@Import(EnableCommandRegistrar.class)
37+
public @interface EnableCommand {
38+
39+
/**
40+
* Defines candicate classes for shell commands.
41+
*
42+
* @return candidate classes for shell commands
43+
*/
44+
Class<?>[] value() default {};
45+
}

spring-shell-core/src/main/java/org/springframework/shell/command/annotation/ExceptionResolver.java

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

24-
import org.springframework.aot.hint.annotation.Reflective;
25-
2624
/**
2725
* Annotation for handling exceptions in specific command classes and/or its methods.
2826
*
@@ -31,7 +29,6 @@
3129
@Retention(RetentionPolicy.RUNTIME)
3230
@Target(ElementType.METHOD)
3331
@Documented
34-
@Reflective
3532
public @interface ExceptionResolver {
3633

3734
/**
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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.command.annotation;
17+
18+
import java.lang.annotation.Documented;
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Retention;
21+
import java.lang.annotation.RetentionPolicy;
22+
import java.lang.annotation.Target;
23+
24+
import org.springframework.shell.command.CommandRegistration.OptionArity;
25+
26+
/**
27+
* Annotation marking a method parameter to be a candicate for an option.
28+
*
29+
* @author Janne Valkealahti
30+
*/
31+
@Retention(RetentionPolicy.RUNTIME)
32+
@Target(ElementType.PARAMETER)
33+
@Documented
34+
public @interface Option {
35+
36+
/**
37+
* Long names of an option. There can be multiple names where first is primary
38+
* one and other are aliases.
39+
*
40+
* @return Option long names, defaults to empty.
41+
*/
42+
String[] longNames() default {};
43+
44+
/**
45+
* Short names of an option. There can be multiple names where first is primary
46+
* one and other are aliases.
47+
*
48+
* @return Option short names, defaults to empty.
49+
*/
50+
char[] shortNames() default {};
51+
52+
/**
53+
* Mark option required.
54+
*
55+
* @return true if option is required, defaults to false.
56+
*/
57+
boolean required() default false;
58+
59+
/**
60+
* Define option default value.
61+
*
62+
* @return default value
63+
*/
64+
String defaultValue() default "";
65+
66+
/**
67+
* Return a short description of the option.
68+
*
69+
* @return description of the option
70+
*/
71+
String description() default "";
72+
73+
/**
74+
* Define option arity.
75+
*
76+
* @return option arity
77+
*/
78+
OptionArity arity() default OptionArity.NONE;
79+
}

0 commit comments

Comments
 (0)