Make ResultHandlers configuration more explicit
Handle exit as a dedicated case (prevents eg 'exit' commands in scripts to make script quit) Add an example of custom ApplicationRunner Fixes #187 Fixes #183 Decouple ApplicationRunners Make ThrowableResultHandler behave differently in non-interactive mode
This commit is contained in:
@@ -118,7 +118,8 @@ public class Shell implements CommandRegistry {
|
||||
* </p>
|
||||
*/
|
||||
public void run(InputProvider inputProvider) throws IOException {
|
||||
while (true) {
|
||||
Object result = null;
|
||||
while (!(result instanceof ExitRequest)) {
|
||||
Input input;
|
||||
try {
|
||||
input = inputProvider.readInput();
|
||||
@@ -130,8 +131,8 @@ public class Shell implements CommandRegistry {
|
||||
if (input == null) {
|
||||
break;
|
||||
}
|
||||
Object result = evaluate(input);
|
||||
if (result != NO_INPUT) {
|
||||
result = evaluate(input);
|
||||
if (result != NO_INPUT && !(result instanceof ExitRequest)) {
|
||||
resultHandler.handleResult(result);
|
||||
}
|
||||
}
|
||||
@@ -155,7 +156,6 @@ public class Shell implements CommandRegistry {
|
||||
String command = findLongestCommand(line);
|
||||
|
||||
List<String> words = input.words();
|
||||
Object result;
|
||||
if (command != null) {
|
||||
MethodTarget methodTarget = methodTargets.get(command);
|
||||
Availability availability = methodTarget.getAvailability();
|
||||
|
||||
@@ -18,14 +18,15 @@ package org.springframework.shell;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConverterFactory;
|
||||
@@ -33,14 +34,11 @@ import org.springframework.core.convert.converter.GenericConverter;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.shell.result.ResultHandlerConfig;
|
||||
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
|
||||
/**
|
||||
* Creates supporting beans for running the Shell
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan(basePackageClasses = ResultHandlerConfig.class)
|
||||
@Import(ResultHandlerConfig.class)
|
||||
public class SpringShellAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -16,11 +16,7 @@
|
||||
|
||||
package org.springframework.shell.jline;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.Reader;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.jline.reader.LineReader;
|
||||
import org.jline.reader.Parser;
|
||||
@@ -30,28 +26,38 @@ import org.jline.utils.AttributedString;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.shell.ExitRequest;
|
||||
import org.springframework.shell.Input;
|
||||
import org.springframework.shell.InputProvider;
|
||||
import org.springframework.shell.Shell;
|
||||
|
||||
/**
|
||||
* Default Boot runner that bootstraps the shell application.
|
||||
* Default Boot runner that bootstraps the shell application in interactive mode.
|
||||
*
|
||||
* <p>
|
||||
* Default implementation has default priority and looks for application arguments that start with an {@literal @},
|
||||
* assuming they are paths to script files. Executes them and quits if they are present, starts the shell interactively
|
||||
* otherwise.
|
||||
* Runs the REPL of the shell unless the {@literal spring.shell.interactive} property has been set to {@literal false}.
|
||||
* </p>
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
//tag::documentation[]
|
||||
@Order(DefaultShellApplicationRunner.PRECEDENCE)
|
||||
public class DefaultShellApplicationRunner implements ApplicationRunner {
|
||||
//end::documentation[]
|
||||
@Order(InteractiveShellApplicationRunner.PRECEDENCE)
|
||||
public class InteractiveShellApplicationRunner implements ApplicationRunner {
|
||||
|
||||
/**
|
||||
* The precedence at which this runner is set. Highger precedence runners may effectively disable this one by setting
|
||||
* the {@link #SPRING_SHELL_INTERACTIVE_ENABLED} property to {@literal false}.
|
||||
*/
|
||||
public static final int PRECEDENCE = 0;
|
||||
|
||||
public static final String SPRING_SHELL_INTERACTIVE = "spring.shell.interactive";
|
||||
public static final String ENABLED = "enabled";
|
||||
|
||||
/** The name of the property that controls whether this runner effectively does something. */
|
||||
public static final String SPRING_SHELL_INTERACTIVE_ENABLED = SPRING_SHELL_INTERACTIVE + "." + ENABLED;
|
||||
|
||||
private final LineReader lineReader;
|
||||
|
||||
private final PromptProvider promptProvider;
|
||||
@@ -60,33 +66,37 @@ public class DefaultShellApplicationRunner implements ApplicationRunner {
|
||||
|
||||
private final Shell shell;
|
||||
|
||||
public DefaultShellApplicationRunner(LineReader lineReader, PromptProvider promptProvider, Parser parser, Shell shell) {
|
||||
private final Environment environment;
|
||||
|
||||
public InteractiveShellApplicationRunner(LineReader lineReader, PromptProvider promptProvider, Parser parser, Shell shell, Environment environment) {
|
||||
this.lineReader = lineReader;
|
||||
this.promptProvider = promptProvider;
|
||||
this.parser = parser;
|
||||
this.shell = shell;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
//tag::documentation[]
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
List<File> scriptsToRun = args.getNonOptionArgs().stream()
|
||||
.filter(s -> s.startsWith("@"))
|
||||
.map(s -> new File(s.substring(1)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (scriptsToRun.isEmpty()) {
|
||||
boolean interactive = isEnabled();
|
||||
if (interactive) {
|
||||
InputProvider inputProvider = new JLineInputProvider(lineReader, promptProvider);
|
||||
shell.run(inputProvider);
|
||||
} else {
|
||||
for (File file : scriptsToRun) {
|
||||
try (Reader reader = new FileReader(file); FileInputProvider inputProvider = new FileInputProvider(reader, parser)) {
|
||||
shell.run(inputProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//end::documentation[]
|
||||
|
||||
public boolean isEnabled() {
|
||||
return environment.getProperty(SPRING_SHELL_INTERACTIVE_ENABLED,boolean.class, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to dynamically disable this runner.
|
||||
*/
|
||||
public static void disable(ConfigurableEnvironment environment) {
|
||||
environment.getPropertySources().addFirst(new MapPropertySource("interactive.override",
|
||||
Collections.singletonMap(SPRING_SHELL_INTERACTIVE_ENABLED, "false")));
|
||||
}
|
||||
|
||||
public static class JLineInputProvider implements InputProvider {
|
||||
|
||||
private final LineReader lineReader;
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.shell.jline;
|
||||
|
||||
import static org.springframework.shell.jline.InteractiveShellApplicationRunner.SPRING_SHELL_INTERACTIVE;
|
||||
import static org.springframework.shell.jline.ScriptShellApplicationRunner.SPRING_SHELL_SCRIPT;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
@@ -33,25 +36,20 @@ import org.jline.utils.AttributedStyle;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.shell.CompletingParsedLine;
|
||||
import org.springframework.shell.CompletionContext;
|
||||
import org.springframework.shell.CompletionProposal;
|
||||
import org.springframework.shell.ExitRequest;
|
||||
import org.springframework.shell.Input;
|
||||
import org.springframework.shell.InputProvider;
|
||||
import org.springframework.shell.ResultHandler;
|
||||
import org.springframework.shell.Shell;
|
||||
|
||||
/**
|
||||
@@ -83,9 +81,15 @@ public class JLineShellAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ApplicationRunner.class)
|
||||
public ApplicationRunner applicationRunner(Parser parser) {
|
||||
return new DefaultShellApplicationRunner(lineReader(), promptProvider, parser, shell);
|
||||
@ConditionalOnProperty(prefix = SPRING_SHELL_INTERACTIVE, value = InteractiveShellApplicationRunner.ENABLED, havingValue = "true", matchIfMissing = true)
|
||||
public ApplicationRunner interactiveApplicationRunner(Parser parser, Environment environment) {
|
||||
return new InteractiveShellApplicationRunner(lineReader(), promptProvider, parser, shell, environment);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = SPRING_SHELL_SCRIPT, value = ScriptShellApplicationRunner.ENABLED, havingValue = "true", matchIfMissing = true)
|
||||
public ApplicationRunner scriptApplicationRunner(Parser parser, ConfigurableEnvironment environment) {
|
||||
return new ScriptShellApplicationRunner(parser, shell, environment);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.jline;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.Reader;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jline.reader.Parser;
|
||||
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.shell.Shell;
|
||||
|
||||
/**
|
||||
* Spring Boot ApplicationRunner that looks for process arguments that start with
|
||||
* {@literal @}, which are then interpreted as references to script files to run and exit.
|
||||
*
|
||||
* <p>
|
||||
* Has higher precedence than {@link InteractiveShellApplicationRunner} so that it
|
||||
* prevents it to run if scripts are found.
|
||||
* </p>
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
//tag::documentation[]
|
||||
@Order(InteractiveShellApplicationRunner.PRECEDENCE - 100) // Runs before InteractiveShellApplicationRunner
|
||||
public class ScriptShellApplicationRunner implements ApplicationRunner {
|
||||
//end::documentation[]
|
||||
|
||||
public static final String SPRING_SHELL_SCRIPT = "spring.shell.script";
|
||||
public static final String ENABLED = "spring.shell.script";
|
||||
|
||||
/**
|
||||
* The name of the environment property that allows to disable the behavior of this
|
||||
* runner.
|
||||
*/
|
||||
public static final String SPRING_SHELL_SCRIPT_ENABLED = SPRING_SHELL_SCRIPT + "." + ENABLED;
|
||||
|
||||
private final Parser parser;
|
||||
|
||||
private final Shell shell;
|
||||
|
||||
private final ConfigurableEnvironment environment;
|
||||
|
||||
public ScriptShellApplicationRunner(Parser parser, Shell shell, ConfigurableEnvironment environment) {
|
||||
this.parser = parser;
|
||||
this.shell = shell;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
//tag::documentation[]
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
List<File> scriptsToRun = args.getNonOptionArgs().stream()
|
||||
.filter(s -> s.startsWith("@"))
|
||||
.map(s -> new File(s.substring(1)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
boolean batchEnabled = environment.getProperty(SPRING_SHELL_SCRIPT_ENABLED, boolean.class, true);
|
||||
|
||||
if (!scriptsToRun.isEmpty() && batchEnabled) {
|
||||
InteractiveShellApplicationRunner.disable(environment);
|
||||
for (File file : scriptsToRun) {
|
||||
try (Reader reader = new FileReader(file);
|
||||
FileInputProvider inputProvider = new FileInputProvider(reader, parser)) {
|
||||
shell.run(inputProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//end::documentation[]
|
||||
|
||||
}
|
||||
@@ -26,7 +26,6 @@ import org.springframework.stereotype.Component;
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
@Component
|
||||
public class AttributedCharSequenceResultHandler extends TerminalAwareResultHandler<AttributedCharSequence> {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -25,11 +25,10 @@ import org.springframework.stereotype.Component;
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
@Component
|
||||
public class DefaultResultHandler implements ResultHandler<Object> {
|
||||
public class DefaultResultHandler extends TerminalAwareResultHandler<Object> {
|
||||
|
||||
@Override
|
||||
public void handleResult(Object result) {
|
||||
System.out.println(String.valueOf(result));
|
||||
protected void doHandleResult(Object result) {
|
||||
terminal.writer().println(String.valueOf(result));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 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
|
||||
*
|
||||
* http://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.result;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.shell.ExitRequest;
|
||||
import org.springframework.shell.ResultHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Intercepts {@link org.springframework.shell.ExitRequest} exceptions and gracefully exits the running process.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
@Component
|
||||
public class ExitRequestResultHandler implements ResultHandler<ExitRequest> {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void handleResult(ExitRequest result) {
|
||||
if (applicationContext instanceof Closeable) {
|
||||
try {
|
||||
((Closeable) applicationContext).close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
System.exit(result.status());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,6 @@ import java.util.stream.StreamSupport;
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
@Component
|
||||
public class ParameterValidationExceptionResultHandler
|
||||
extends TerminalAwareResultHandler<ParameterValidationException> {
|
||||
|
||||
|
||||
@@ -55,4 +55,24 @@ public class ResultHandlerConfig {
|
||||
return new TerminalSizeAwareResultHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AttributedCharSequenceResultHandler attributedCharSequenceResultHandler() {
|
||||
return new AttributedCharSequenceResultHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DefaultResultHandler defaultResultHandler() {
|
||||
return new DefaultResultHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ParameterValidationExceptionResultHandler parameterValidationExceptionResultHandler() {
|
||||
return new ParameterValidationExceptionResultHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ThrowableResultHandler throwableResultHandler() {
|
||||
return new ThrowableResultHandler();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.shell.CommandRegistry;
|
||||
import org.springframework.shell.ResultHandler;
|
||||
import org.springframework.shell.jline.InteractiveShellApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -34,7 +35,6 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
@Component
|
||||
public class ThrowableResultHandler extends TerminalAwareResultHandler<Throwable> {
|
||||
|
||||
/**
|
||||
@@ -47,13 +47,16 @@ public class ThrowableResultHandler extends TerminalAwareResultHandler<Throwable
|
||||
@Autowired @Lazy
|
||||
private CommandRegistry commandRegistry;
|
||||
|
||||
@Autowired @Lazy
|
||||
private InteractiveShellApplicationRunner interactiveRunner;
|
||||
|
||||
@Override
|
||||
protected void doHandleResult(Throwable result) {
|
||||
lastError = result;
|
||||
String toPrint = StringUtils.hasLength(result.getMessage()) ? result.getMessage() : result.toString();
|
||||
terminal.writer().println(new AttributedString(toPrint,
|
||||
AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)).toAnsi());
|
||||
if (commandRegistry.listCommands().containsKey(DETAILS_COMMAND_NAME)) {
|
||||
if (interactiveRunner.isEnabled() && commandRegistry.listCommands().containsKey(DETAILS_COMMAND_NAME)) {
|
||||
terminal.writer().println(
|
||||
new AttributedStringBuilder()
|
||||
.append("Details of the error have been omitted. You can use the ", AttributedStyle.DEFAULT.foreground(AttributedStyle.RED))
|
||||
@@ -63,6 +66,17 @@ public class ThrowableResultHandler extends TerminalAwareResultHandler<Throwable
|
||||
);
|
||||
}
|
||||
terminal.writer().flush();
|
||||
if (!interactiveRunner.isEnabled()) {
|
||||
if (result instanceof RuntimeException) {
|
||||
throw (RuntimeException) result;
|
||||
}
|
||||
else if (result instanceof Error) {
|
||||
throw (Error) result;
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException((Throwable) result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,13 +47,7 @@ public class TypeHierarchyResultHandler implements ResultHandler<Object> {
|
||||
}
|
||||
Class<?> clazz = result.getClass();
|
||||
ResultHandler handler = getResultHandler(clazz);
|
||||
try {
|
||||
handler.handleResult(result);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Protect against exceptions happening in ResultHandlers
|
||||
getResultHandler(e.getClass()).handleResult(e);
|
||||
}
|
||||
handler.handleResult(result);
|
||||
}
|
||||
|
||||
private ResultHandler getResultHandler(Class<?> clazz) {
|
||||
|
||||
@@ -864,20 +864,20 @@ public class CustomPromptProvider implements PromptProvider {
|
||||
----
|
||||
|
||||
==== Customizing Command Line Options Behavior
|
||||
Spring Shell comes with a default Spring Boot `ApplicationRunner`
|
||||
that bootstraps the Shell REPL. It sets up the JLine infrastructure and eventually
|
||||
calls `Shell.run()`.
|
||||
Spring Shell comes with two default Spring Boot `ApplicationRunners`:
|
||||
|
||||
If the application is started with arguments that start with `@` though, it assumes those
|
||||
are local file names and tries to run commands contained in those files (with the same
|
||||
semantics as the xref:script-command[script command]) and then exits the process.
|
||||
* `InteractiveShellApplicationRunner` bootstraps the Shell REPL. It sets up the JLine infrastructure and eventually
|
||||
calls `Shell.run()`
|
||||
* `ScriptShellApplicationRunner` looks for program arguments that start with `@`, assumes those are local file names and
|
||||
tries to run commands contained in those files (with the same semantics as the xref:script-command[script command]) and
|
||||
then exits the process (by effectively disabling the `InteractiveShellApplicationRunner`, see below).
|
||||
|
||||
If this behavior does not suit you, simply provide one (or more) bean of type `ApplicationRunner`
|
||||
and it will replace the default. You'll want to take inspiration from the `DefaultShellApplicationRunner`:
|
||||
and optionally disable the standard ones. You'll want to take inspiration from the `ScriptShellApplicationRunner`:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../../spring-shell-core/src/main/java/org/springframework/shell/jline/DefaultShellApplicationRunner.java[tag=documentation]
|
||||
include::../../../../spring-shell-core/src/main/java/org/springframework/shell/jline/ScriptShellApplicationRunner.java[tag=documentation]
|
||||
|
||||
...
|
||||
----
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.ExitCodeExceptionMapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.shell.ExitRequest;
|
||||
import org.springframework.shell.Input;
|
||||
import org.springframework.shell.InputProvider;
|
||||
import org.springframework.shell.Shell;
|
||||
import org.springframework.shell.jline.InteractiveShellApplicationRunner;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Configuration
|
||||
public class ExampleApplicationRunnerConfiguration {
|
||||
|
||||
@Autowired
|
||||
private Shell shell;
|
||||
|
||||
@Bean
|
||||
public CommandLineRunner exampleCommandLineRunner(ConfigurableEnvironment environment) {
|
||||
return new ExampleCommandLineRunner(shell, environment);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ExitCodeExceptionMapper exitCodeExceptionMapper() {
|
||||
return exception -> {
|
||||
Throwable e = exception;
|
||||
while (e != null && !(e instanceof ExitRequest)) {
|
||||
e = e.getCause();
|
||||
}
|
||||
return e == null ? 1 : ((ExitRequest) e).status();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Example CommandLineRunner that shows how overall shell behavior can be customized. In
|
||||
* this particular example, any program (process) arguments are assumed to be shell
|
||||
* commands that need to be executed (and the shell then quits).
|
||||
*/
|
||||
@Order(InteractiveShellApplicationRunner.PRECEDENCE - 2)
|
||||
class ExampleCommandLineRunner implements CommandLineRunner {
|
||||
|
||||
private Shell shell;
|
||||
|
||||
private final ConfigurableEnvironment environment;
|
||||
|
||||
public ExampleCommandLineRunner(Shell shell, ConfigurableEnvironment environment) {
|
||||
this.shell = shell;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
List<String> commandsToRun = Arrays.stream(args)
|
||||
.filter(w -> !w.startsWith("@"))
|
||||
.collect(Collectors.toList());
|
||||
if (!commandsToRun.isEmpty()) {
|
||||
InteractiveShellApplicationRunner.disable(environment);
|
||||
shell.run(new StringInputProvider(commandsToRun));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StringInputProvider implements InputProvider {
|
||||
|
||||
private final List<String> words;
|
||||
|
||||
private boolean done;
|
||||
|
||||
public StringInputProvider(List<String> words) {
|
||||
this.words = words;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Input readInput() {
|
||||
if (!done) {
|
||||
done = true;
|
||||
return new Input() {
|
||||
@Override
|
||||
public List<String> words() {
|
||||
return words;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rawText() {
|
||||
return StringUtils.collectionToDelimitedString(words, " ");
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,8 +36,6 @@ public class CommandValueProvider extends ValueProviderSupport {
|
||||
|
||||
private final CommandRegistry commandRegistry;
|
||||
|
||||
@Lazy
|
||||
@Autowired
|
||||
public CommandValueProvider(CommandRegistry commandRegistry) {
|
||||
this.commandRegistry = commandRegistry;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.shell.standard;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.shell.CommandRegistry;
|
||||
import org.springframework.shell.MethodTargetRegistrar;
|
||||
@@ -33,7 +34,7 @@ import org.springframework.shell.ParameterResolver;
|
||||
public class StandardAPIAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public ValueProvider commandValueProvider(CommandRegistry commandRegistry) {
|
||||
public ValueProvider commandValueProvider(@Lazy CommandRegistry commandRegistry) {
|
||||
return new CommandValueProvider(commandRegistry);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user