Support for non-interactive shell commands
- Add support for running shell commands as a non-interactive mode. - This works by adding new ShellApplicationRunner interface which is an extension to ApplicationRunner forcing to have exactly one main ApplicationRunner and then DefaultApplicationRunner dispatches to new interface ShellRunner which allows to pick between script, interactive and non-interactive, etc. - It is sort of a breaking change but works much better not having a need to have previous hooks between application runners to disable things at runtime. - All this makes it closer for a user to have a choice between using shell commands as is without entering interactive mode. - Also add SpringShellProperties for better config props support for boot users. - Fixes #342
This commit is contained in:
@@ -15,46 +15,23 @@
|
||||
*/
|
||||
package org.springframework.shell.boot;
|
||||
|
||||
import org.jline.reader.LineReader;
|
||||
import org.jline.reader.Parser;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.shell.Shell;
|
||||
import org.springframework.shell.jline.InteractiveShellApplicationRunner;
|
||||
import org.springframework.shell.jline.PromptProvider;
|
||||
import org.springframework.shell.jline.ScriptShellApplicationRunner;
|
||||
|
||||
import static org.springframework.shell.jline.InteractiveShellApplicationRunner.SPRING_SHELL_INTERACTIVE;
|
||||
import static org.springframework.shell.jline.ScriptShellApplicationRunner.SPRING_SHELL_SCRIPT;
|
||||
import org.springframework.shell.DefaultApplicationRunner;
|
||||
import org.springframework.shell.ShellApplicationRunner;
|
||||
import org.springframework.shell.ShellRunner;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(SpringShellProperties.class)
|
||||
public class ApplicationRunnerAutoConfiguration {
|
||||
|
||||
private Shell shell;
|
||||
|
||||
private PromptProvider promptProvider;
|
||||
|
||||
private LineReader lineReader;
|
||||
|
||||
public ApplicationRunnerAutoConfiguration(Shell shell, PromptProvider promptProvider, LineReader lineReader) {
|
||||
this.shell = shell;
|
||||
this.promptProvider = promptProvider;
|
||||
this.lineReader = lineReader;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = SPRING_SHELL_INTERACTIVE, value = InteractiveShellApplicationRunner.ENABLED, havingValue = "true", matchIfMissing = true)
|
||||
public InteractiveShellApplicationRunner interactiveApplicationRunner(Environment environment) {
|
||||
return new InteractiveShellApplicationRunner(lineReader, promptProvider, shell, environment);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = SPRING_SHELL_SCRIPT, value = ScriptShellApplicationRunner.ENABLED, havingValue = "true", matchIfMissing = true)
|
||||
public ScriptShellApplicationRunner scriptApplicationRunner(Parser parser, ConfigurableEnvironment environment) {
|
||||
return new ScriptShellApplicationRunner(parser, shell, environment);
|
||||
@ConditionalOnMissingBean(ShellApplicationRunner.class)
|
||||
public DefaultApplicationRunner defaultApplicationRunner(List<ShellRunner> shellRunners) {
|
||||
return new DefaultApplicationRunner(shellRunners);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2021 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.boot;
|
||||
|
||||
import org.jline.reader.LineReader;
|
||||
import org.jline.reader.Parser;
|
||||
|
||||
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.jline.InteractiveShellApplicationRunner;
|
||||
import org.springframework.shell.jline.NonInteractiveShellApplicationRunner;
|
||||
import org.springframework.shell.jline.PromptProvider;
|
||||
import org.springframework.shell.jline.ScriptShellApplicationRunner;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class ShellRunnerAutoConfiguration {
|
||||
|
||||
private Shell shell;
|
||||
private PromptProvider promptProvider;
|
||||
private LineReader lineReader;
|
||||
private Parser parser;
|
||||
|
||||
public ShellRunnerAutoConfiguration(Shell shell, PromptProvider promptProvider, LineReader lineReader, Parser parser) {
|
||||
this.shell = shell;
|
||||
this.promptProvider = promptProvider;
|
||||
this.lineReader = lineReader;
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "spring.shell.interactive", value = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
public InteractiveShellApplicationRunner interactiveApplicationRunner() {
|
||||
return new InteractiveShellApplicationRunner(lineReader, promptProvider, shell);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "spring.shell.noninteractive", value = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
public NonInteractiveShellApplicationRunner nonInteractiveApplicationRunner() {
|
||||
return new NonInteractiveShellApplicationRunner(shell);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "spring.shell.script", value = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
public ScriptShellApplicationRunner scriptApplicationRunner() {
|
||||
return new ScriptShellApplicationRunner(parser, shell);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright 2021 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.boot;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Configuration properties for shell.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.shell")
|
||||
public class SpringShellProperties {
|
||||
|
||||
private Script script = new Script();
|
||||
private Interactive interactive = new Interactive();
|
||||
private Noninteractive noninteractive = new Noninteractive();
|
||||
private Command command = new Command();
|
||||
|
||||
public void setScript(Script script) {
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
public Script getScript() {
|
||||
return script;
|
||||
}
|
||||
|
||||
public void setInteractive(Interactive interactive) {
|
||||
this.interactive = interactive;
|
||||
}
|
||||
|
||||
public Interactive getInteractive() {
|
||||
return interactive;
|
||||
}
|
||||
|
||||
public Noninteractive getNoninteractive() {
|
||||
return noninteractive;
|
||||
}
|
||||
|
||||
public void setNoninteractive(Noninteractive noninteractive) {
|
||||
this.noninteractive = noninteractive;
|
||||
}
|
||||
|
||||
public Command getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public void setCommand(Command command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public static class Script {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Interactive {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Noninteractive {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public static class HelpCommand {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClearCommand {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public static class QuitCommand {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public static class StacktraceCommand {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ScriptCommand {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public static class HistoryCommand {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Command {
|
||||
|
||||
private HelpCommand help = new HelpCommand();
|
||||
private ClearCommand clear = new ClearCommand();
|
||||
private QuitCommand quit = new QuitCommand();
|
||||
private StacktraceCommand stacktrace = new StacktraceCommand();
|
||||
private ScriptCommand script = new ScriptCommand();
|
||||
private HistoryCommand history = new HistoryCommand();
|
||||
|
||||
public void setHelp(HelpCommand help) {
|
||||
this.help = help;
|
||||
}
|
||||
|
||||
public HelpCommand getHelp() {
|
||||
return help;
|
||||
}
|
||||
|
||||
public ClearCommand getClear() {
|
||||
return clear;
|
||||
}
|
||||
|
||||
public void setClear(ClearCommand clear) {
|
||||
this.clear = clear;
|
||||
}
|
||||
|
||||
public QuitCommand getQuit() {
|
||||
return quit;
|
||||
}
|
||||
|
||||
public void setQuit(QuitCommand quit) {
|
||||
this.quit = quit;
|
||||
}
|
||||
|
||||
public StacktraceCommand getStacktrace() {
|
||||
return stacktrace;
|
||||
}
|
||||
|
||||
public void setStacktrace(StacktraceCommand stacktrace) {
|
||||
this.stacktrace = stacktrace;
|
||||
}
|
||||
|
||||
public ScriptCommand getScript() {
|
||||
return script;
|
||||
}
|
||||
|
||||
public void setScript(ScriptCommand script) {
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
public HistoryCommand getHistory() {
|
||||
return history;
|
||||
}
|
||||
|
||||
public void setHistory(HistoryCommand history) {
|
||||
this.history = history;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.shell.boot.SpringShellAutoConfiguration,\
|
||||
org.springframework.shell.boot.ShellRunnerAutoConfiguration,\
|
||||
org.springframework.shell.boot.ApplicationRunnerAutoConfiguration,\
|
||||
org.springframework.shell.boot.CommandRegistryAutoConfiguration,\
|
||||
org.springframework.shell.boot.LineReaderAutoConfiguration,\
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2021 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.boot;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.env.SystemEnvironmentPropertySource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class SpringShellPropertiesTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
|
||||
|
||||
@Test
|
||||
public void defaultNoPropertiesSet() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(Config1.class)
|
||||
.run((context) -> {
|
||||
SpringShellProperties properties = context.getBean(SpringShellProperties.class);
|
||||
assertThat(properties.getScript().isEnabled()).isTrue();
|
||||
assertThat(properties.getInteractive().isEnabled()).isTrue();
|
||||
assertThat(properties.getNoninteractive().isEnabled()).isTrue();
|
||||
assertThat(properties.getCommand().getClear().isEnabled()).isTrue();
|
||||
assertThat(properties.getCommand().getHelp().isEnabled()).isTrue();
|
||||
assertThat(properties.getCommand().getHistory().isEnabled()).isTrue();
|
||||
assertThat(properties.getCommand().getQuit().isEnabled()).isTrue();
|
||||
assertThat(properties.getCommand().getScript().isEnabled()).isTrue();
|
||||
assertThat(properties.getCommand().getStacktrace().isEnabled()).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setProperties() {
|
||||
this.contextRunner
|
||||
.withInitializer(context -> {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("spring.shell.script.enabled", "false");
|
||||
map.put("spring.shell.interactive.enabled", "false");
|
||||
map.put("spring.shell.noninteractive.enabled", "false");
|
||||
map.put("spring.shell.command.clear.enabled", "false");
|
||||
map.put("spring.shell.command.help.enabled", "false");
|
||||
map.put("spring.shell.command.history.enabled", "false");
|
||||
map.put("spring.shell.command.quit.enabled", "false");
|
||||
map.put("spring.shell.command.script.enabled", "false");
|
||||
map.put("spring.shell.command.stacktrace.enabled", "false");
|
||||
context.getEnvironment().getPropertySources().addLast(new SystemEnvironmentPropertySource(
|
||||
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, map));
|
||||
})
|
||||
.withUserConfiguration(Config1.class)
|
||||
.run((context) -> {
|
||||
SpringShellProperties properties = context.getBean(SpringShellProperties.class);
|
||||
assertThat(properties.getScript().isEnabled()).isFalse();
|
||||
assertThat(properties.getInteractive().isEnabled()).isFalse();
|
||||
assertThat(properties.getNoninteractive().isEnabled()).isFalse();
|
||||
assertThat(properties.getCommand().getClear().isEnabled()).isFalse();
|
||||
assertThat(properties.getCommand().getHelp().isEnabled()).isFalse();
|
||||
assertThat(properties.getCommand().getHistory().isEnabled()).isFalse();
|
||||
assertThat(properties.getCommand().getQuit().isEnabled()).isFalse();
|
||||
assertThat(properties.getCommand().getScript().isEnabled()).isFalse();
|
||||
assertThat(properties.getCommand().getStacktrace().isEnabled()).isFalse();
|
||||
});
|
||||
}
|
||||
|
||||
@EnableConfigurationProperties({ SpringShellProperties.class })
|
||||
private static class Config1 {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user