diff --git a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/CommandRegistryAutoConfiguration.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/CommandRegistryAutoConfiguration.java index 26e7e198..105be462 100644 --- a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/CommandRegistryAutoConfiguration.java +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/CommandRegistryAutoConfiguration.java @@ -21,14 +21,16 @@ import org.springframework.context.annotation.Configuration; import org.springframework.shell.CommandRegistry; import org.springframework.shell.ConfigurableCommandRegistry; import org.springframework.shell.MethodTargetRegistrar; +import org.springframework.shell.context.ShellContext; @Configuration(proxyBeanMethods = false) public class CommandRegistryAutoConfiguration { @Bean public CommandRegistry commandRegistry( - ObjectProvider methodTargetRegistrars) { - ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(); + ObjectProvider methodTargetRegistrars, + ShellContext shellContext) { + ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(shellContext); methodTargetRegistrars.orderedStream().forEach(resolver -> { resolver.register(registry); }); diff --git a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ShellContextAutoConfiguration.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ShellContextAutoConfiguration.java new file mode 100644 index 00000000..7755eaf7 --- /dev/null +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ShellContextAutoConfiguration.java @@ -0,0 +1,15 @@ +package org.springframework.shell.boot; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.shell.context.DefaultShellContext; +import org.springframework.shell.context.ShellContext; + +@Configuration(proxyBeanMethods = false) +public class ShellContextAutoConfiguration { + + @Bean + public ShellContext shellContext() { + return new DefaultShellContext(); + } +} diff --git a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ShellRunnerAutoConfiguration.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ShellRunnerAutoConfiguration.java index f580b18a..ff11d62f 100644 --- a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ShellRunnerAutoConfiguration.java +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ShellRunnerAutoConfiguration.java @@ -22,6 +22,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.shell.Shell; +import org.springframework.shell.context.ShellContext; import org.springframework.shell.jline.InteractiveShellRunner; import org.springframework.shell.jline.NonInteractiveShellRunner; import org.springframework.shell.jline.PromptProvider; @@ -34,24 +35,27 @@ public class ShellRunnerAutoConfiguration { private PromptProvider promptProvider; private LineReader lineReader; private Parser parser; + private ShellContext shellContext; - public ShellRunnerAutoConfiguration(Shell shell, PromptProvider promptProvider, LineReader lineReader, Parser parser) { + public ShellRunnerAutoConfiguration(Shell shell, PromptProvider promptProvider, LineReader lineReader, + Parser parser, ShellContext shellContext) { this.shell = shell; this.promptProvider = promptProvider; this.lineReader = lineReader; this.parser = parser; + this.shellContext = shellContext; } @Bean @ConditionalOnProperty(prefix = "spring.shell.interactive", value = "enabled", havingValue = "true", matchIfMissing = true) public InteractiveShellRunner interactiveApplicationRunner() { - return new InteractiveShellRunner(lineReader, promptProvider, shell); + return new InteractiveShellRunner(lineReader, promptProvider, shell, shellContext); } @Bean @ConditionalOnProperty(prefix = "spring.shell.noninteractive", value = "enabled", havingValue = "true", matchIfMissing = true) public NonInteractiveShellRunner nonInteractiveApplicationRunner() { - return new NonInteractiveShellRunner(shell); + return new NonInteractiveShellRunner(shell, shellContext); } @Bean diff --git a/spring-shell-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-shell-autoconfigure/src/main/resources/META-INF/spring.factories index 49dc20f1..4022caba 100644 --- a/spring-shell-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-shell-autoconfigure/src/main/resources/META-INF/spring.factories @@ -1,4 +1,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.springframework.shell.boot.ShellContextAutoConfiguration,\ org.springframework.shell.boot.SpringShellAutoConfiguration,\ org.springframework.shell.boot.ShellRunnerAutoConfiguration,\ org.springframework.shell.boot.ApplicationRunnerAutoConfiguration,\ diff --git a/spring-shell-core/src/main/java/org/springframework/shell/ConfigurableCommandRegistry.java b/spring-shell-core/src/main/java/org/springframework/shell/ConfigurableCommandRegistry.java index 43e4f340..46797477 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/ConfigurableCommandRegistry.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/ConfigurableCommandRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 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,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.shell; import java.util.HashMap; import java.util.Map; -import java.util.TreeMap; +import java.util.stream.Collectors; + +import org.springframework.shell.context.InteractionMode; +import org.springframework.shell.context.ShellContext; /** * A {@link CommandRegistry} that supports registration of new commands. @@ -29,11 +31,31 @@ import java.util.TreeMap; */ public class ConfigurableCommandRegistry implements CommandRegistry { + private final ShellContext shellContext; private Map commands = new HashMap<>(); + public ConfigurableCommandRegistry(ShellContext shellContext) { + this.shellContext = shellContext; + } + @Override public Map listCommands() { - return new TreeMap<>(commands); + return commands.entrySet().stream() + .filter(e -> { + InteractionMode mim = e.getValue().getInteractionMode(); + InteractionMode cim = shellContext.getInteractionMode(); + if (mim == null || cim == null || mim == InteractionMode.ALL) { + return true; + } + else if (mim == InteractionMode.INTERACTIVE) { + return cim == InteractionMode.INTERACTIVE || cim == InteractionMode.ALL; + } + else if (mim == InteractionMode.NONINTERACTIVE) { + return cim == InteractionMode.NONINTERACTIVE || cim == InteractionMode.ALL; + } + return true; + }) + .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())); } public void register(String name, MethodTarget target) { diff --git a/spring-shell-core/src/main/java/org/springframework/shell/MethodTarget.java b/spring-shell-core/src/main/java/org/springframework/shell/MethodTarget.java index b478eb07..0eced9cd 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/MethodTarget.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/MethodTarget.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-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; import java.lang.reflect.Method; @@ -21,6 +20,7 @@ import java.util.HashSet; import java.util.Set; import java.util.function.Supplier; +import org.springframework.shell.context.InteractionMode; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; @@ -37,6 +37,8 @@ public class MethodTarget implements Command { private final Help help; + private final InteractionMode interactionMode; + /** * If not null, returns whether or not the command is currently available. Implementations must be idempotent. */ @@ -51,6 +53,10 @@ public class MethodTarget implements Command { } public MethodTarget(Method method, Object bean, Help help, Supplier availabilityIndicator) { + this(method, bean, help, availabilityIndicator, null); + } + + public MethodTarget(Method method, Object bean, Help help, Supplier availabilityIndicator, InteractionMode interactionMode) { Assert.notNull(method, "Method cannot be null"); Assert.notNull(bean, "Bean cannot be null"); Assert.hasText(help.getDescription(), String.format("Help cannot be blank when trying to define command based on '%s'", method)); @@ -59,6 +65,7 @@ public class MethodTarget implements Command { this.bean = bean; this.help = help; this.availabilityIndicator = availabilityIndicator != null ? availabilityIndicator : () -> Availability.available(); + this.interactionMode = interactionMode; } /** @@ -103,6 +110,10 @@ public class MethodTarget implements Command { return availabilityIndicator.get(); } + public InteractionMode getInteractionMode() { + return interactionMode; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/spring-shell-core/src/main/java/org/springframework/shell/context/DefaultShellContext.java b/spring-shell-core/src/main/java/org/springframework/shell/context/DefaultShellContext.java new file mode 100644 index 00000000..0a47f935 --- /dev/null +++ b/spring-shell-core/src/main/java/org/springframework/shell/context/DefaultShellContext.java @@ -0,0 +1,39 @@ +/* + * 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.context; + +import org.springframework.util.Assert; + +/** + * Default implementation of a {@link ShellContext}. + * + * @author Janne Valkealahti + */ +public class DefaultShellContext implements ShellContext { + + private InteractionMode interactionMode = InteractionMode.ALL; + + @Override + public InteractionMode getInteractionMode() { + return interactionMode; + } + + @Override + public void setInteractionMode(InteractionMode interactionMode) { + Assert.notNull(interactionMode, "mode cannot be null"); + this.interactionMode = interactionMode; + } +} diff --git a/spring-shell-core/src/main/java/org/springframework/shell/context/InteractionMode.java b/spring-shell-core/src/main/java/org/springframework/shell/context/InteractionMode.java new file mode 100644 index 00000000..ffb5f019 --- /dev/null +++ b/spring-shell-core/src/main/java/org/springframework/shell/context/InteractionMode.java @@ -0,0 +1,41 @@ +/* + * 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.context; + +/** + * Enumeration for modes shell is operating. + * + * @author Janne Valkealahti + */ +public enum InteractionMode { + + /** + * All possible modes. + */ + ALL, + + /** + * Non-interactive mode which is expected to exit and doesn't have any kind of + * running mode to keep shell alive. + */ + NONINTERACTIVE, + + /** + * Interactive mode which is expected to not exit and do have a running mode to + * keep shell alive. + */ + INTERACTIVE +} diff --git a/spring-shell-core/src/main/java/org/springframework/shell/context/ShellContext.java b/spring-shell-core/src/main/java/org/springframework/shell/context/ShellContext.java new file mode 100644 index 00000000..bad55da6 --- /dev/null +++ b/spring-shell-core/src/main/java/org/springframework/shell/context/ShellContext.java @@ -0,0 +1,39 @@ +/* + * 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.context; + +/** + * Interface defining a contract for a context which allows to loosely connect + * different components together and keep things alive between commands. + * + * @author Janne Valkealahti + */ +public interface ShellContext { + + /** + * Gets an interaction mode. + * + * @return a current interaction mode + */ + InteractionMode getInteractionMode(); + + /** + * Sets an interaction mode. + * + * @param interactionMode the interaction mode + */ + void setInteractionMode(InteractionMode interactionMode); +} diff --git a/spring-shell-core/src/main/java/org/springframework/shell/jline/InteractiveShellRunner.java b/spring-shell-core/src/main/java/org/springframework/shell/jline/InteractiveShellRunner.java index 0163fdec..a0fdfd07 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/jline/InteractiveShellRunner.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/jline/InteractiveShellRunner.java @@ -27,6 +27,8 @@ import org.springframework.shell.Input; import org.springframework.shell.InputProvider; import org.springframework.shell.Shell; import org.springframework.shell.ShellRunner; +import org.springframework.shell.context.InteractionMode; +import org.springframework.shell.context.ShellContext; /** * Default Boot runner that bootstraps the shell application in interactive @@ -52,14 +54,19 @@ public class InteractiveShellRunner implements ShellRunner { private final Shell shell; - public InteractiveShellRunner(LineReader lineReader, PromptProvider promptProvider, Shell shell) { + private final ShellContext shellContext; + + public InteractiveShellRunner(LineReader lineReader, PromptProvider promptProvider, Shell shell, + ShellContext shellContext) { this.lineReader = lineReader; this.promptProvider = promptProvider; this.shell = shell; + this.shellContext = shellContext; } @Override public void run(ApplicationArguments args) throws Exception { + shellContext.setInteractionMode(InteractionMode.INTERACTIVE); InputProvider inputProvider = new JLineInputProvider(lineReader, promptProvider); shell.run(inputProvider); } diff --git a/spring-shell-core/src/main/java/org/springframework/shell/jline/NonInteractiveShellRunner.java b/spring-shell-core/src/main/java/org/springframework/shell/jline/NonInteractiveShellRunner.java index 1f08021e..90c4ea42 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/jline/NonInteractiveShellRunner.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/jline/NonInteractiveShellRunner.java @@ -24,6 +24,8 @@ import org.springframework.shell.Input; import org.springframework.shell.InputProvider; import org.springframework.shell.Shell; import org.springframework.shell.ShellRunner; +import org.springframework.shell.context.InteractionMode; +import org.springframework.shell.context.ShellContext; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -37,9 +39,11 @@ import org.springframework.util.StringUtils; public class NonInteractiveShellRunner implements ShellRunner { private final Shell shell; + private final ShellContext shellContext; - public NonInteractiveShellRunner(Shell shell) { + public NonInteractiveShellRunner(Shell shell, ShellContext shellContext) { this.shell = shell; + this.shellContext = shellContext; } @Override @@ -50,6 +54,7 @@ public class NonInteractiveShellRunner implements ShellRunner { @Override public void run(ApplicationArguments args) throws Exception { + shellContext.setInteractionMode(InteractionMode.NONINTERACTIVE); List argsToShellCommand = Arrays.asList(args.getSourceArgs()); InputProvider inputProvider = new StringInputProvider(argsToShellCommand); shell.run(inputProvider); diff --git a/spring-shell-core/src/test/java/org/springframework/shell/ConfigurableCommandRegistryTest.java b/spring-shell-core/src/test/java/org/springframework/shell/ConfigurableCommandRegistryTest.java index 62aee668..88dbcd60 100644 --- a/spring-shell-core/src/test/java/org/springframework/shell/ConfigurableCommandRegistryTest.java +++ b/spring-shell-core/src/test/java/org/springframework/shell/ConfigurableCommandRegistryTest.java @@ -18,6 +18,8 @@ package org.springframework.shell; import org.junit.jupiter.api.Test; +import org.springframework.shell.context.DefaultShellContext; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -30,14 +32,14 @@ public class ConfigurableCommandRegistryTest { @Test public void testRegistration() { - ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(); + ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(new DefaultShellContext()); registry.register("foo", MethodTarget.of("toString", this, new Command.Help("some command"))); assertThat(registry.listCommands()).containsKeys("foo"); } @Test public void testDoubleRegistration() { - ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(); + ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(new DefaultShellContext()); registry.register("foo", MethodTarget.of("toString", this, new Command.Help("some command"))); assertThatThrownBy(() -> { diff --git a/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Clear.java b/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Clear.java index 336056a6..52bc8f47 100644 --- a/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Clear.java +++ b/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Clear.java @@ -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. @@ -18,6 +18,7 @@ package org.springframework.shell.standard.commands; import org.jline.utils.InfoCmp; +import org.springframework.shell.context.InteractionMode; import org.springframework.shell.standard.AbstractShellComponent; import org.springframework.shell.standard.ShellComponent; import org.springframework.shell.standard.ShellMethod; @@ -47,7 +48,7 @@ public class Clear extends AbstractShellComponent { public Clear() { } - @ShellMethod("Clear the shell screen.") + @ShellMethod(value = "Clear the shell screen.", interactionMode = InteractionMode.INTERACTIVE) public void clear() { getTerminal().puts(InfoCmp.Capability.clear_screen); } diff --git a/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Quit.java b/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Quit.java index 07669c17..906a5e95 100644 --- a/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Quit.java +++ b/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Quit.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 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. @@ -17,6 +17,7 @@ package org.springframework.shell.standard.commands; import org.springframework.shell.ExitRequest; +import org.springframework.shell.context.InteractionMode; import org.springframework.shell.standard.ShellComponent; import org.springframework.shell.standard.ShellMethod; @@ -41,7 +42,7 @@ public class Quit { */ public interface Command {} - @ShellMethod(value = "Exit the shell.", key = {"quit", "exit"}) + @ShellMethod(value = "Exit the shell.", key = {"quit", "exit"}, interactionMode = InteractionMode.INTERACTIVE) public void quit() { throw new ExitRequest(); } diff --git a/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Stacktrace.java b/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Stacktrace.java index d7badd29..f0c676b1 100644 --- a/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Stacktrace.java +++ b/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Stacktrace.java @@ -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. @@ -16,6 +16,7 @@ package org.springframework.shell.standard.commands; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.shell.context.InteractionMode; import org.springframework.shell.result.ThrowableResultHandler; import org.springframework.shell.standard.AbstractShellComponent; import org.springframework.shell.standard.ShellComponent; @@ -49,7 +50,9 @@ public class Stacktrace extends AbstractShellComponent { this.throwableResultHandler = throwableResultHandler; } - @ShellMethod(key = ThrowableResultHandler.DETAILS_COMMAND_NAME, value = "Display the full stacktrace of the last error.") + @ShellMethod(key = ThrowableResultHandler.DETAILS_COMMAND_NAME, + value = "Display the full stacktrace of the last error.", + interactionMode = InteractionMode.INTERACTIVE) public void stacktrace() { if (throwableResultHandler.getIfAvailable().getLastError() != null) { throwableResultHandler.getIfAvailable().getLastError().printStackTrace(getTerminal().writer()); diff --git a/spring-shell-standard/src/main/java/org/springframework/shell/standard/ShellMethod.java b/spring-shell-standard/src/main/java/org/springframework/shell/standard/ShellMethod.java index e296284b..8e6b9563 100644 --- a/spring-shell-standard/src/main/java/org/springframework/shell/standard/ShellMethod.java +++ b/spring-shell-standard/src/main/java/org/springframework/shell/standard/ShellMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-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.standard; import java.lang.annotation.Documented; @@ -22,6 +21,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.springframework.shell.context.InteractionMode; + /** * Used to mark a method as invokable via Spring Shell. * @@ -68,4 +69,14 @@ public @interface ShellMethod { */ String group() default INHERITED; + /** + * Defines interaction mode for a command as a hint when command should be + * available. For example presense of some commands doesn't make sense if shell + * is running as non-interactive mode and vice versa. + * + * Defaults to {@link InteractionMode#ALL} + * + * @return interaction mode + */ + InteractionMode interactionMode() default InteractionMode.ALL; } diff --git a/spring-shell-standard/src/main/java/org/springframework/shell/standard/StandardMethodTargetRegistrar.java b/spring-shell-standard/src/main/java/org/springframework/shell/standard/StandardMethodTargetRegistrar.java index 683e43b6..adb3a2bf 100644 --- a/spring-shell-standard/src/main/java/org/springframework/shell/standard/StandardMethodTargetRegistrar.java +++ b/spring-shell-standard/src/main/java/org/springframework/shell/standard/StandardMethodTargetRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2017 the original author or authors. + * Copyright 2015-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.standard; import java.lang.reflect.Method; @@ -76,7 +75,8 @@ public class StandardMethodTargetRegistrar implements MethodTargetRegistrar, App String group = getOrInferGroup(method); for (String key : keys) { Supplier availabilityIndicator = findAvailabilityIndicator(keys, bean, method); - MethodTarget target = new MethodTarget(method, bean, new Command.Help(shellMapping.value(), group), availabilityIndicator); + MethodTarget target = new MethodTarget(method, bean, new Command.Help(shellMapping.value(), group), + availabilityIndicator, shellMapping.interactionMode()); registry.register(key, target); commands.put(key, target); } diff --git a/spring-shell-standard/src/test/java/org/springframework/shell/standard/StandardMethodTargetRegistrarTest.java b/spring-shell-standard/src/test/java/org/springframework/shell/standard/StandardMethodTargetRegistrarTest.java index eb561f48..0e6f18bf 100644 --- a/spring-shell-standard/src/test/java/org/springframework/shell/standard/StandardMethodTargetRegistrarTest.java +++ b/spring-shell-standard/src/test/java/org/springframework/shell/standard/StandardMethodTargetRegistrarTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 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. @@ -26,6 +26,8 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.shell.Availability; import org.springframework.shell.ConfigurableCommandRegistry; import org.springframework.shell.MethodTarget; +import org.springframework.shell.context.DefaultShellContext; +import org.springframework.shell.context.InteractionMode; import org.springframework.shell.standard.test1.GroupOneCommands; import org.springframework.shell.standard.test2.GroupThreeCommands; import org.springframework.shell.standard.test2.GroupTwoCommands; @@ -42,7 +44,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; public class StandardMethodTargetRegistrarTest { private StandardMethodTargetRegistrar registrar = new StandardMethodTargetRegistrar(); - private ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(); + private ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(new DefaultShellContext()); @Test public void testRegistrations() { @@ -256,4 +258,47 @@ public class StandardMethodTargetRegistrarTest { Assertions.assertThat(commands.get("implicit3").getGroup()).isEqualTo("Explicit Group 3 Class Level"); } + @Test + public void testInteractionModeInteractive() { + DefaultShellContext shellContext = new DefaultShellContext(); + shellContext.setInteractionMode(InteractionMode.INTERACTIVE); + registry = new ConfigurableCommandRegistry(shellContext); + ApplicationContext applicationContext = new AnnotationConfigApplicationContext(InteractionModeCommands.class); + registrar.setApplicationContext(applicationContext); + registrar.register(registry); + + assertThat(registry.listCommands().get("foo1")).isNotNull(); + assertThat(registry.listCommands().get("foo2")).isNull(); + assertThat(registry.listCommands().get("foo3")).isNotNull(); + } + + @Test + public void testInteractionModeNonInteractive() { + DefaultShellContext shellContext = new DefaultShellContext(); + shellContext.setInteractionMode(InteractionMode.NONINTERACTIVE); + registry = new ConfigurableCommandRegistry(shellContext); + ApplicationContext applicationContext = new AnnotationConfigApplicationContext(InteractionModeCommands.class); + registrar.setApplicationContext(applicationContext); + registrar.register(registry); + + assertThat(registry.listCommands().get("foo1")).isNull(); + assertThat(registry.listCommands().get("foo2")).isNotNull(); + assertThat(registry.listCommands().get("foo3")).isNotNull(); + } + + @ShellComponent + public static class InteractionModeCommands { + + @ShellMethod(value = "foo1", interactionMode = InteractionMode.INTERACTIVE) + public void foo1() { + } + + @ShellMethod(value = "foo2", interactionMode = InteractionMode.NONINTERACTIVE) + public void foo2() { + } + + @ShellMethod(value = "foo3") + public void foo3() { + } + } } diff --git a/spring-shell-standard/src/test/java/org/springframework/shell/standard/completion/AbstractCompletionsTests.java b/spring-shell-standard/src/test/java/org/springframework/shell/standard/completion/AbstractCompletionsTests.java index 5f7d4381..989bd4e3 100644 --- a/spring-shell-standard/src/test/java/org/springframework/shell/standard/completion/AbstractCompletionsTests.java +++ b/spring-shell-standard/src/test/java/org/springframework/shell/standard/completion/AbstractCompletionsTests.java @@ -29,6 +29,7 @@ import org.springframework.shell.CommandRegistry; import org.springframework.shell.ConfigurableCommandRegistry; import org.springframework.shell.MethodTarget; import org.springframework.shell.ParameterResolver; +import org.springframework.shell.context.DefaultShellContext; import org.springframework.shell.standard.ShellMethod; import org.springframework.shell.standard.ShellOption; import org.springframework.shell.standard.StandardParameterResolver; @@ -42,7 +43,7 @@ public class AbstractCompletionsTests { @Test public void testBasicModelGeneration() { DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); - ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry(); + ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry(new DefaultShellContext()); List parameterResolvers = new ArrayList<>(); StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(), Collections.emptySet()); @@ -91,7 +92,7 @@ public class AbstractCompletionsTests { @Test public void testBuilder() { DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); - ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry(); + ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry(new DefaultShellContext()); List parameterResolvers = new ArrayList<>(); TestCompletions completions = new TestCompletions(resourceLoader, commandRegistry, parameterResolvers); diff --git a/spring-shell-standard/src/test/java/org/springframework/shell/standard/completion/BashCompletionsTests.java b/spring-shell-standard/src/test/java/org/springframework/shell/standard/completion/BashCompletionsTests.java index 9d8b1d25..8a906bbe 100644 --- a/spring-shell-standard/src/test/java/org/springframework/shell/standard/completion/BashCompletionsTests.java +++ b/spring-shell-standard/src/test/java/org/springframework/shell/standard/completion/BashCompletionsTests.java @@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.shell.ConfigurableCommandRegistry; import org.springframework.shell.ParameterResolver; +import org.springframework.shell.context.DefaultShellContext; import static org.assertj.core.api.Assertions.assertThat; @@ -48,7 +49,7 @@ public class BashCompletionsTests { @Test public void testDoesNotError() { - ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry(); + ConfigurableCommandRegistry commandRegistry = new ConfigurableCommandRegistry(new DefaultShellContext()); List parameterResolvers = new ArrayList<>(); BashCompletions completions = new BashCompletions(context, commandRegistry, parameterResolvers); String bash = completions.generate("root-command"); diff --git a/spring-shell-test-samples/src/test/java/com/example/test/functional/CalculatorCommandsTest.java b/spring-shell-test-samples/src/test/java/com/example/test/functional/CalculatorCommandsTest.java index 647065fa..0da6d4a6 100644 --- a/spring-shell-test-samples/src/test/java/com/example/test/functional/CalculatorCommandsTest.java +++ b/spring-shell-test-samples/src/test/java/com/example/test/functional/CalculatorCommandsTest.java @@ -12,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.shell.ConfigurableCommandRegistry; import org.springframework.shell.MethodTarget; +import org.springframework.shell.context.DefaultShellContext; import org.springframework.shell.standard.StandardMethodTargetRegistrar; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -32,7 +33,7 @@ public class CalculatorCommandsTest extends BaseCalculatorTest { private static final Class COMMAND_CLASS_UNDER_TEST = CalculatorCommands.class; - private final ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(); + private final ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(new DefaultShellContext()); @Autowired private CalculatorState state;