Rework command subsystem
- Focus of these changes are to introduce a new command system based on real registrations (new way) instead of continuously (old way) resolve methods and its parameters via reflection. - There's a lot of changes as this resolution via reflection had its hooks almost everywhere and thus most changes are just refactorings. - Order to understand real changes I'd start to look classes under `org.springframework.shell.command` package as it defines new registration, catalog and parser classes. Also samples contain new classes to demonstrate new functionality. - Fixes #380
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2021 the original author or authors.
|
||||
* Copyright 2017-2022 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.
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.shell.samples;
|
||||
|
||||
import org.jline.utils.AttributedString;
|
||||
@@ -30,6 +29,7 @@ import org.springframework.shell.jline.PromptProvider;
|
||||
* <p>Creates the application context and start the REPL.</p>
|
||||
*
|
||||
* @author Eric Bottard
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class SpringShellSample {
|
||||
|
||||
@@ -71,6 +71,11 @@ public class Commands {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
@ShellMethod("Concat strings.")
|
||||
public String concat(String a, String b, String c) {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
@ShellMethod("Fails with an exception. Shows enum conversion.")
|
||||
public void fail(ElementType elementType) {
|
||||
throw new IllegalArgumentException("You said " + elementType);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2022 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.shell.samples.standard;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
|
||||
@Configuration
|
||||
public class FunctionCommands {
|
||||
|
||||
@Bean
|
||||
public CommandRegistration commandRegistration1() {
|
||||
return CommandRegistration.builder()
|
||||
.command("function", "command1")
|
||||
.help("function sample")
|
||||
.group("Function Commands")
|
||||
.withTarget()
|
||||
.function(ctx -> {
|
||||
String arg1 = ctx.getOptionValue("arg1");
|
||||
return String.format("hi, arg1 value is '%s'", arg1);
|
||||
})
|
||||
.and()
|
||||
.withOption()
|
||||
.longNames("arg1")
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistration commandRegistration2() {
|
||||
return CommandRegistration.builder()
|
||||
.command("function", "command2")
|
||||
.help("function sample")
|
||||
.group("Function Commands")
|
||||
.withTarget()
|
||||
.function(ctx -> {
|
||||
Boolean a = ctx.getOptionValue("a");
|
||||
Boolean b = ctx.getOptionValue("b");
|
||||
Boolean c = ctx.getOptionValue("c");
|
||||
return String.format("hi, boolean values for a, b, c are '%s' '%s' '%s'", a, b, c);
|
||||
})
|
||||
.and()
|
||||
.withOption()
|
||||
.shortNames('a')
|
||||
.type(boolean.class)
|
||||
.and()
|
||||
.withOption()
|
||||
.shortNames('b')
|
||||
.type(boolean.class)
|
||||
.and()
|
||||
.withOption()
|
||||
.shortNames('c')
|
||||
.type(boolean.class)
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandRegistration commandRegistration3() {
|
||||
return CommandRegistration.builder()
|
||||
.command("function", "command3")
|
||||
.help("function sample")
|
||||
.group("Function Commands")
|
||||
.withTarget()
|
||||
.consumer(ctx -> {
|
||||
String arg1 = ctx.getOptionValue("arg1");
|
||||
ctx.getTerminal().writer()
|
||||
.println(String.format("hi, arg1 value is '%s'", arg1));
|
||||
})
|
||||
.and()
|
||||
.withOption()
|
||||
.longNames("arg1")
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
package org.springframework.shell.samples.standard;
|
||||
|
||||
import org.springframework.shell.MethodTarget;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.shell.command.CommandContext;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
import org.springframework.shell.standard.AbstractShellComponent;
|
||||
import org.springframework.shell.standard.ShellComponent;
|
||||
import org.springframework.shell.standard.ShellMethod;
|
||||
@@ -24,42 +27,90 @@ import org.springframework.shell.standard.ShellOption;
|
||||
@ShellComponent
|
||||
public class RegisterCommands extends AbstractShellComponent {
|
||||
|
||||
private final static String GROUP = "Register Commands";
|
||||
private final PojoMethods pojoMethods = new PojoMethods();
|
||||
private final CommandRegistration registered1;
|
||||
private final CommandRegistration registered2;
|
||||
private final CommandRegistration registered3;
|
||||
|
||||
@ShellMethod(key = "register add", value = "Register commands", group = "Register Commands")
|
||||
public RegisterCommands() {
|
||||
registered1 = CommandRegistration.builder()
|
||||
.command("register registered1")
|
||||
.group(GROUP)
|
||||
.help("registered1 command")
|
||||
.withTarget()
|
||||
.method(pojoMethods, "registered1")
|
||||
.and()
|
||||
.build();
|
||||
registered2 = CommandRegistration.builder()
|
||||
.command("register registered2")
|
||||
.help("registered2 command")
|
||||
.group(GROUP)
|
||||
.withTarget()
|
||||
.method(pojoMethods, "registered2")
|
||||
.and()
|
||||
.withOption()
|
||||
.longNames("arg1")
|
||||
.and()
|
||||
.build();
|
||||
registered3 = CommandRegistration.builder()
|
||||
.command("register registered3")
|
||||
.help("registered3 command")
|
||||
.group(GROUP)
|
||||
.withTarget()
|
||||
.method(pojoMethods, "registered3")
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
|
||||
@ShellMethod(key = "register add", value = "Register commands", group = GROUP)
|
||||
public String register() {
|
||||
MethodTarget target1 = MethodTarget.of("dynamic1", pojoMethods, "Dynamic1 command", "Register Commands");
|
||||
MethodTarget target2 = MethodTarget.of("dynamic2", pojoMethods, "Dynamic2 command", "Register Commands");
|
||||
MethodTarget target3 = MethodTarget.of("dynamic3", pojoMethods, "Dynamic3 command", "Register Commands");
|
||||
getCommandRegistry().addCommand("register dynamic1", target1);
|
||||
getCommandRegistry().addCommand("register dynamic2", target2);
|
||||
getCommandRegistry().addCommand("register dynamic3", target3);
|
||||
return "Registered commands dynamic1, dynamic2, dynamic3";
|
||||
getCommandCatalog().register(registered1, registered2, registered3);
|
||||
registerFunctionCommand("register registered4");
|
||||
return "Registered commands registered1, registered2, registered3, registered4";
|
||||
}
|
||||
|
||||
@ShellMethod(key = "register remove", value = "Deregister commands", group = "Register Commands")
|
||||
@ShellMethod(key = "register remove", value = "Deregister commands", group = GROUP)
|
||||
public String deregister() {
|
||||
getCommandRegistry().removeCommand("register dynamic1");
|
||||
getCommandRegistry().removeCommand("register dynamic2");
|
||||
getCommandRegistry().removeCommand("register dynamic3");
|
||||
return "Deregistered commands dynamic1, dynamic2, dynamic3";
|
||||
getCommandCatalog().unregister("register registered1", "register registered2", "register registered3",
|
||||
"register registered4");
|
||||
return "Deregistered commands registered1, registered2, registered3, registered4";
|
||||
}
|
||||
|
||||
private void registerFunctionCommand(String command) {
|
||||
Function<CommandContext, String> function = ctx -> {
|
||||
String arg1 = ctx.getOptionValue("arg1");
|
||||
return String.format("hi, arg1 value is '%s'", arg1);
|
||||
};
|
||||
CommandRegistration registration = CommandRegistration.builder()
|
||||
.command(command)
|
||||
.help("registered4 command")
|
||||
.group(GROUP)
|
||||
.withTarget()
|
||||
.function(function)
|
||||
.and()
|
||||
.withOption()
|
||||
.longNames("arg1")
|
||||
.and()
|
||||
.build();
|
||||
getCommandCatalog().register(registration);
|
||||
}
|
||||
|
||||
public static class PojoMethods {
|
||||
|
||||
@ShellMethod
|
||||
public String dynamic1() {
|
||||
return "dynamic1";
|
||||
public String registered1() {
|
||||
return "registered1";
|
||||
}
|
||||
|
||||
@ShellMethod
|
||||
public String dynamic2(String arg1) {
|
||||
return "dynamic2" + arg1;
|
||||
public String registered2(String arg1) {
|
||||
return "registered2" + arg1;
|
||||
}
|
||||
|
||||
@ShellMethod
|
||||
public String dynamic3(@ShellOption(defaultValue = ShellOption.NULL) String arg1) {
|
||||
return "dynamic3" + arg1;
|
||||
public String registered3(@ShellOption(defaultValue = ShellOption.NULL) String arg1) {
|
||||
return "registered3" + arg1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2022 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.shell.samples.standard;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.shell.command.CommandRegistration;
|
||||
import org.springframework.shell.command.CommandResolver;
|
||||
import org.springframework.shell.standard.ShellComponent;
|
||||
import org.springframework.shell.standard.ShellMethod;
|
||||
|
||||
public class ResolvedCommands {
|
||||
|
||||
private static final String GROUP = "Resolve Commands";
|
||||
|
||||
@Configuration
|
||||
public static class ResolvedCommandsConfiguration {
|
||||
|
||||
@Bean
|
||||
Server1CommandResolver server1CommandResolver() {
|
||||
return new Server1CommandResolver();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Server2CommandResolver server2CommandResolver() {
|
||||
return new Server2CommandResolver();
|
||||
}
|
||||
}
|
||||
|
||||
@ShellComponent
|
||||
public static class ResolvedCommandsCommands {
|
||||
|
||||
private final Server1CommandResolver server1CommandResolver;
|
||||
private final Server2CommandResolver server2CommandResolver;
|
||||
|
||||
ResolvedCommandsCommands(Server1CommandResolver server1CommandResolver,
|
||||
Server2CommandResolver server2CommandResolver) {
|
||||
this.server1CommandResolver = server1CommandResolver;
|
||||
this.server2CommandResolver = server2CommandResolver;
|
||||
}
|
||||
|
||||
@ShellMethod(key = "resolve enableserver1", group = GROUP)
|
||||
public String server1Enable() {
|
||||
server1CommandResolver.enabled = true;
|
||||
return "Enabled server1";
|
||||
}
|
||||
|
||||
@ShellMethod(key = "resolve disableserver1", group = GROUP)
|
||||
public String server1Disable() {
|
||||
server1CommandResolver.enabled = false;
|
||||
return "Disabled server1";
|
||||
}
|
||||
|
||||
@ShellMethod(key = "resolve enableserver2", group = GROUP)
|
||||
public String server2Enable() {
|
||||
server2CommandResolver.enabled = true;
|
||||
return "Enabled server2";
|
||||
}
|
||||
|
||||
@ShellMethod(key = "resolve disableserver2", group = GROUP)
|
||||
public String server2Disable() {
|
||||
server2CommandResolver.enabled = false;
|
||||
return "Disabled server2";
|
||||
}
|
||||
}
|
||||
|
||||
static class Server1CommandResolver implements CommandResolver {
|
||||
|
||||
private final List<CommandRegistration> registrations = new ArrayList<>();
|
||||
boolean enabled = false;
|
||||
|
||||
Server1CommandResolver() {
|
||||
CommandRegistration resolved1 = CommandRegistration.builder()
|
||||
.command("resolve server1 command1")
|
||||
.group(GROUP)
|
||||
.help("server1 command1")
|
||||
.withTarget()
|
||||
.function(ctx -> {
|
||||
return "hi from server1 command1";
|
||||
})
|
||||
.and()
|
||||
.build();
|
||||
registrations.add(resolved1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommandRegistration> resolve() {
|
||||
return enabled ? registrations : Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
static class Server2CommandResolver implements CommandResolver {
|
||||
|
||||
private final List<CommandRegistration> registrations = new ArrayList<>();
|
||||
boolean enabled = false;
|
||||
|
||||
Server2CommandResolver() {
|
||||
CommandRegistration resolved1 = CommandRegistration.builder()
|
||||
.command("resolve server2 command1")
|
||||
.group(GROUP)
|
||||
.help("server2 command1")
|
||||
.withTarget()
|
||||
.function(ctx -> {
|
||||
return "hi from server2 command1";
|
||||
})
|
||||
.and()
|
||||
.build();
|
||||
CommandRegistration resolved2 = CommandRegistration.builder()
|
||||
.command("resolve server2 command2")
|
||||
.group(GROUP)
|
||||
.help("server2 command2")
|
||||
.withTarget()
|
||||
.function(ctx -> {
|
||||
return "hi from server2 command2";
|
||||
})
|
||||
.and()
|
||||
.build();
|
||||
registrations.add(resolved1);
|
||||
registrations.add(resolved2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommandRegistration> resolve() {
|
||||
return enabled ? registrations : Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user