From f6394a453117d738385495081ab3c34cfc08cba5 Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Fri, 24 Dec 2021 08:50:40 +0000 Subject: [PATCH] Rework bean handling - Lot of rework to move better model to work around bean cycles - Remove use of @Lazy - Move StandardAPIAutoConfiguration to autoconfig package - Remove some of a direct ObjectProvider use in constructors - Adds spring-native support with most of a things working out of a box - Relates #324 - Relates #329 - Relates #323 --- .../ApplicationRunnerAutoConfiguration.java | 17 +++-- .../CommandRegistryAutoConfiguration.java | 4 +- .../boot/CompleterAutoConfiguration.java | 2 +- ...derParameterResolverAutoConfiguration.java | 2 +- .../shell/boot/JLineAutoConfiguration.java | 2 +- .../boot/JLineShellAutoConfiguration.java | 2 +- .../boot/LineReaderAutoConfiguration.java | 23 +++--- .../ParameterResolverAutoConfiguration.java | 25 ++++++ .../boot/SpringShellAutoConfiguration.java | 17 +---- .../boot}/StandardAPIAutoConfiguration.java | 20 ++--- .../StandardCommandsAutoConfiguration.java | 19 ++--- .../main/resources/META-INF/spring.factories | 2 + .../java/org/springframework/shell/Shell.java | 7 +- .../java/org/springframework/shell/Utils.java | 30 ++++++++ .../AttributedCharSequenceResultHandler.java | 5 ++ .../shell/result/DefaultResultHandler.java | 6 ++ ...meterValidationExceptionResultHandler.java | 5 ++ .../shell/result/ResultHandlerConfig.java | 26 ++++--- .../result/TerminalAwareResultHandler.java | 6 +- .../TerminalSizeAwareResultHandler.java | 5 ++ .../shell/result/ThrowableResultHandler.java | 19 +++-- .../JCommanderParameterResolver.java | 4 +- .../shell/samples/noautoconf/NoAutoConf.java | 2 +- .../shell/standard/commands/Clear.java | 15 ++-- .../shell/standard/commands/Help.java | 32 ++------ .../shell/standard/commands/Script.java | 28 +++++-- .../shell/standard/commands/Stacktrace.java | 27 ++++--- .../shell/standard/commands/HelpTest.java | 6 +- .../standard/AbstractShellComponent.java | 76 +++++++++++++++++++ .../StandardMethodTargetRegistrar.java | 25 ++++-- .../standard/StandardParameterResolver.java | 13 +--- .../main/resources/META-INF/spring.factories | 2 - .../StandardParameterResolverTest.java | 36 ++++++++- 33 files changed, 342 insertions(+), 168 deletions(-) create mode 100644 spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ParameterResolverAutoConfiguration.java rename {spring-shell-standard/src/main/java/org/springframework/shell/standard => spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot}/StandardAPIAutoConfiguration.java (70%) create mode 100644 spring-shell-standard/src/main/java/org/springframework/shell/standard/AbstractShellComponent.java delete mode 100644 spring-shell-standard/src/main/resources/META-INF/spring.factories diff --git a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ApplicationRunnerAutoConfiguration.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ApplicationRunnerAutoConfiguration.java index e31d54d4..ed591d73 100644 --- a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ApplicationRunnerAutoConfiguration.java +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ApplicationRunnerAutoConfiguration.java @@ -18,8 +18,6 @@ package org.springframework.shell.boot; import org.jline.reader.LineReader; import org.jline.reader.Parser; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.ApplicationRunner; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -33,27 +31,30 @@ 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; -@Configuration +@Configuration(proxyBeanMethods = false) public class ApplicationRunnerAutoConfiguration { - @Autowired private Shell shell; - @Autowired private PromptProvider promptProvider; - @Autowired 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 ApplicationRunner interactiveApplicationRunner(Environment environment) { + 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 ApplicationRunner scriptApplicationRunner(Parser parser, ConfigurableEnvironment environment) { + public ScriptShellApplicationRunner scriptApplicationRunner(Parser parser, ConfigurableEnvironment environment) { return new ScriptShellApplicationRunner(parser, shell, environment); } } 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 8563fab8..26e7e198 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 @@ -27,9 +27,9 @@ public class CommandRegistryAutoConfiguration { @Bean public CommandRegistry commandRegistry( - ObjectProvider methodTargerRegistrars) { + ObjectProvider methodTargetRegistrars) { ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(); - methodTargerRegistrars.orderedStream().forEach(resolver -> { + methodTargetRegistrars.orderedStream().forEach(resolver -> { resolver.register(registry); }); return registry; diff --git a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/CompleterAutoConfiguration.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/CompleterAutoConfiguration.java index 7d77e2ea..2f16fd9a 100644 --- a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/CompleterAutoConfiguration.java +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/CompleterAutoConfiguration.java @@ -31,7 +31,7 @@ import org.springframework.shell.CompletionContext; import org.springframework.shell.CompletionProposal; import org.springframework.shell.Shell; -@Configuration +@Configuration(proxyBeanMethods = false) public class CompleterAutoConfiguration { @Autowired diff --git a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/JCommanderParameterResolverAutoConfiguration.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/JCommanderParameterResolverAutoConfiguration.java index da8681a1..e33d07b3 100644 --- a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/JCommanderParameterResolverAutoConfiguration.java +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/JCommanderParameterResolverAutoConfiguration.java @@ -28,7 +28,7 @@ import org.springframework.context.annotation.Bean; * * @author Eric Bottard */ -@Configuration +@Configuration(proxyBeanMethods = false) @ConditionalOnClass({ JCommander.class, JCommanderParameterResolver.class }) public class JCommanderParameterResolverAutoConfiguration { diff --git a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/JLineAutoConfiguration.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/JLineAutoConfiguration.java index a0a560ea..386881a3 100644 --- a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/JLineAutoConfiguration.java +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/JLineAutoConfiguration.java @@ -22,7 +22,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -@Configuration +@Configuration(proxyBeanMethods = false) public class JLineAutoConfiguration { @Configuration diff --git a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/JLineShellAutoConfiguration.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/JLineShellAutoConfiguration.java index 1fdcfbdb..b918a438 100644 --- a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/JLineShellAutoConfiguration.java +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/JLineShellAutoConfiguration.java @@ -37,7 +37,7 @@ import org.springframework.shell.jline.PromptProvider; * @author Eric Bottard * @author Florent Biville */ -@Configuration +@Configuration(proxyBeanMethods = false) public class JLineShellAutoConfiguration { @Bean(destroyMethod = "close") diff --git a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/LineReaderAutoConfiguration.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/LineReaderAutoConfiguration.java index c2f8ea8c..386c526b 100644 --- a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/LineReaderAutoConfiguration.java +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/LineReaderAutoConfiguration.java @@ -29,7 +29,6 @@ import org.jline.utils.AttributedString; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -37,32 +36,36 @@ import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.EventListener; import org.springframework.shell.CommandRegistry; -@Configuration +@Configuration(proxyBeanMethods = false) public class LineReaderAutoConfiguration { - @Autowired private Terminal terminal; - @Autowired private Completer completer; - @Autowired private Parser parser; - @Autowired private CommandRegistry commandRegistry; - @Autowired private org.jline.reader.History jLineHistory; + @Value("${spring.application.name:spring-shell}.log") + private String historyPath; + + public LineReaderAutoConfiguration(Terminal terminal, Completer completer, Parser parser, + CommandRegistry commandRegistry, org.jline.reader.History jLineHistory) { + this.terminal = terminal; + this.completer = completer; + this.parser = parser; + this.commandRegistry = commandRegistry; + this.jLineHistory = jLineHistory; + } + @EventListener public void onContextClosedEvent(ContextClosedEvent event) throws IOException { jLineHistory.save(); } - @Value("${spring.application.name:spring-shell}.log") - private String historyPath; - @Bean public LineReader lineReader() { LineReaderBuilder lineReaderBuilder = LineReaderBuilder.builder() diff --git a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ParameterResolverAutoConfiguration.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ParameterResolverAutoConfiguration.java new file mode 100644 index 00000000..c25e6b95 --- /dev/null +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ParameterResolverAutoConfiguration.java @@ -0,0 +1,25 @@ +package org.springframework.shell.boot; + +import java.util.Set; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.ConversionService; +import org.springframework.shell.ParameterResolver; +import org.springframework.shell.standard.StandardParameterResolver; +import org.springframework.shell.standard.ValueProvider; + +@Configuration(proxyBeanMethods = false) +public class ParameterResolverAutoConfiguration { + + @Bean + public ParameterResolver standardParameterResolver(@Qualifier("spring-shell") ConversionService conversionService, + ObjectProvider valueProviders) { + Set collect = valueProviders.orderedStream().collect(Collectors.toSet()); + return new StandardParameterResolver(conversionService, collect); + } + +} diff --git a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/SpringShellAutoConfiguration.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/SpringShellAutoConfiguration.java index 5b3ee8cb..8ed73f37 100644 --- a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/SpringShellAutoConfiguration.java +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/SpringShellAutoConfiguration.java @@ -19,11 +19,7 @@ package org.springframework.shell.boot; import java.util.Collection; import java.util.Set; -import javax.validation.Validation; -import javax.validation.Validator; - import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -33,6 +29,7 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterFactory; import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.shell.CommandRegistry; import org.springframework.shell.ResultHandler; import org.springframework.shell.ResultHandlerService; import org.springframework.shell.Shell; @@ -42,7 +39,7 @@ import org.springframework.shell.result.ResultHandlerConfig; /** * Creates supporting beans for running the Shell */ -@Configuration +@Configuration(proxyBeanMethods = false) @Import(ResultHandlerConfig.class) public class SpringShellAutoConfiguration { @@ -66,12 +63,6 @@ public class SpringShellAutoConfiguration { return defaultConversionService; } - @Bean - @ConditionalOnMissingBean(Validator.class) - public Validator validator() { - return Validation.buildDefaultValidatorFactory().getValidator(); - } - @Bean public ResultHandlerService resultHandlerService(Set> resultHandlers) { GenericResultHandlerService service = new GenericResultHandlerService(); @@ -82,7 +73,7 @@ public class SpringShellAutoConfiguration { } @Bean - public Shell shell(ResultHandlerService resultHandlerService) { - return new Shell(resultHandlerService); + public Shell shell(ResultHandlerService resultHandlerService, CommandRegistry commandRegistry) { + return new Shell(resultHandlerService, commandRegistry); } } diff --git a/spring-shell-standard/src/main/java/org/springframework/shell/standard/StandardAPIAutoConfiguration.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/StandardAPIAutoConfiguration.java similarity index 70% rename from spring-shell-standard/src/main/java/org/springframework/shell/standard/StandardAPIAutoConfiguration.java rename to spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/StandardAPIAutoConfiguration.java index a72fe355..bd6398ee 100644 --- a/spring-shell-standard/src/main/java/org/springframework/shell/standard/StandardAPIAutoConfiguration.java +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/StandardAPIAutoConfiguration.java @@ -14,27 +14,28 @@ * limitations under the License. */ -package org.springframework.shell.standard; +package org.springframework.shell.boot; -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; -import org.springframework.shell.ParameterResolver; +import org.springframework.shell.standard.CommandValueProvider; +import org.springframework.shell.standard.EnumValueProvider; +import org.springframework.shell.standard.FileValueProvider; +import org.springframework.shell.standard.StandardMethodTargetRegistrar; +import org.springframework.shell.standard.ValueProvider; /** * Sets up all required beans for supporting the standard Shell API. * * @author Eric Bottard */ -@Configuration +@Configuration(proxyBeanMethods = false) public class StandardAPIAutoConfiguration { @Bean - public ValueProvider commandValueProvider(@Lazy CommandRegistry commandRegistry) { + public ValueProvider commandValueProvider(CommandRegistry commandRegistry) { return new CommandValueProvider(commandRegistry); } @@ -52,9 +53,4 @@ public class StandardAPIAutoConfiguration { public MethodTargetRegistrar standardMethodTargetResolver() { return new StandardMethodTargetRegistrar(); } - - @Bean - public ParameterResolver standardParameterResolver(@Qualifier("spring-shell") ConversionService conversionService) { - return new StandardParameterResolver(conversionService); - } } diff --git a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/StandardCommandsAutoConfiguration.java b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/StandardCommandsAutoConfiguration.java index fcd792bf..eb2b7f9c 100644 --- a/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/StandardCommandsAutoConfiguration.java +++ b/spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/StandardCommandsAutoConfiguration.java @@ -16,8 +16,6 @@ package org.springframework.shell.boot; -import java.util.List; - import org.jline.reader.Parser; import org.springframework.beans.factory.ObjectProvider; @@ -26,8 +24,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.shell.ParameterResolver; -import org.springframework.shell.Shell; +import org.springframework.shell.result.ThrowableResultHandler; import org.springframework.shell.standard.commands.Clear; import org.springframework.shell.standard.commands.Help; import org.springframework.shell.standard.commands.History; @@ -40,15 +37,15 @@ import org.springframework.shell.standard.commands.Stacktrace; * * @author Eric Bottard */ -@Configuration +@Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Help.Command.class }) public class StandardCommandsAutoConfiguration { @Bean @ConditionalOnMissingBean(Help.Command.class) @ConditionalOnProperty(prefix = "spring.shell.command.help", value = "enabled", havingValue = "true", matchIfMissing = true) - public Help help(List parameterResolvers) { - return new Help(parameterResolvers); + public Help help() { + return new Help(); } @Bean @@ -68,15 +65,15 @@ public class StandardCommandsAutoConfiguration { @Bean @ConditionalOnMissingBean(Stacktrace.Command.class) @ConditionalOnProperty(prefix = "spring.shell.command.stacktrace", value = "enabled", havingValue = "true", matchIfMissing = true) - public Stacktrace stacktrace() { - return new Stacktrace(); + public Stacktrace stacktrace(ObjectProvider throwableResultHandler) { + return new Stacktrace(throwableResultHandler); } @Bean @ConditionalOnMissingBean(Script.Command.class) @ConditionalOnProperty(prefix = "spring.shell.command.script", value = "enabled", havingValue = "true", matchIfMissing = true) - public Script script(ObjectProvider shell, Parser parser) { - return new Script(shell, parser); + public Script script(Parser parser) { + return new Script(parser); } @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 ce2992e4..79efc39c 100644 --- a/spring-shell-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-shell-autoconfigure/src/main/resources/META-INF/spring.factories @@ -7,4 +7,6 @@ org.springframework.shell.boot.CompleterAutoConfiguration,\ org.springframework.shell.boot.JLineAutoConfiguration,\ org.springframework.shell.boot.JLineShellAutoConfiguration,\ org.springframework.shell.boot.JCommanderParameterResolverAutoConfiguration,\ +org.springframework.shell.boot.ParameterResolverAutoConfiguration,\ +org.springframework.shell.boot.StandardAPIAutoConfiguration,\ org.springframework.shell.boot.StandardCommandsAutoConfiguration diff --git a/spring-shell-core/src/main/java/org/springframework/shell/Shell.java b/spring-shell-core/src/main/java/org/springframework/shell/Shell.java index fe4b94ad..1a2b7c61 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/Shell.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/Shell.java @@ -30,7 +30,6 @@ import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.validation.ConstraintViolation; -import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; @@ -70,10 +69,9 @@ public class Shell { @Autowired protected ApplicationContext applicationContext; - @Autowired private CommandRegistry commandRegistry; - private Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + private Validator validator = Utils.defaultValidator(); protected Map methodTargets = new HashMap<>(); @@ -85,8 +83,9 @@ public class Shell { */ protected static final Object UNRESOLVED = new Object(); - public Shell(ResultHandlerService resultHandlerService) { + public Shell(ResultHandlerService resultHandlerService, CommandRegistry commandRegistry) { this.resultHandlerService = resultHandlerService; + this.commandRegistry = commandRegistry; } @Autowired(required = false) diff --git a/spring-shell-core/src/main/java/org/springframework/shell/Utils.java b/spring-shell-core/src/main/java/org/springframework/shell/Utils.java index 0d1634e9..d991a620 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/Utils.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/Utils.java @@ -25,6 +25,10 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.MethodParameter; @@ -100,4 +104,30 @@ public class Utils { .collect(Collectors.toList()); return words; } + + private final static ValidatorFactory DEFAULT_VALIDATOR_FACTORY; + private final static Validator DEFAULT_VALIDATOR; + + static { + DEFAULT_VALIDATOR_FACTORY = Validation.buildDefaultValidatorFactory(); + DEFAULT_VALIDATOR = DEFAULT_VALIDATOR_FACTORY.getValidator(); + } + + /** + * Gets a default shared validation factory. + * + * @return default validation factory + */ + public static ValidatorFactory defaultValidatorFactory() { + return DEFAULT_VALIDATOR_FACTORY; + } + + /** + * Gets a default shared validator. + * + * @return default validator + */ + public static Validator defaultValidator() { + return DEFAULT_VALIDATOR; + } } diff --git a/spring-shell-core/src/main/java/org/springframework/shell/result/AttributedCharSequenceResultHandler.java b/spring-shell-core/src/main/java/org/springframework/shell/result/AttributedCharSequenceResultHandler.java index 81c8ed4f..1372d65c 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/result/AttributedCharSequenceResultHandler.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/result/AttributedCharSequenceResultHandler.java @@ -16,6 +16,7 @@ package org.springframework.shell.result; +import org.jline.terminal.Terminal; import org.jline.utils.AttributedCharSequence; import org.springframework.shell.ResultHandler; @@ -27,6 +28,10 @@ import org.springframework.shell.ResultHandler; */ public class AttributedCharSequenceResultHandler extends TerminalAwareResultHandler { + public AttributedCharSequenceResultHandler(Terminal terminal) { + super(terminal); + } + @Override protected void doHandleResult(AttributedCharSequence result) { terminal.writer().println(result.toAnsi(terminal)); diff --git a/spring-shell-core/src/main/java/org/springframework/shell/result/DefaultResultHandler.java b/spring-shell-core/src/main/java/org/springframework/shell/result/DefaultResultHandler.java index 5317af80..c58a985d 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/result/DefaultResultHandler.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/result/DefaultResultHandler.java @@ -16,6 +16,8 @@ package org.springframework.shell.result; +import org.jline.terminal.Terminal; + import org.springframework.shell.ResultHandler; /** @@ -26,6 +28,10 @@ import org.springframework.shell.ResultHandler; */ public class DefaultResultHandler extends TerminalAwareResultHandler { + public DefaultResultHandler(Terminal terminal) { + super(terminal); + } + @Override protected void doHandleResult(Object result) { terminal.writer().println(String.valueOf(result)); diff --git a/spring-shell-core/src/main/java/org/springframework/shell/result/ParameterValidationExceptionResultHandler.java b/spring-shell-core/src/main/java/org/springframework/shell/result/ParameterValidationExceptionResultHandler.java index 7ffbce6b..2532f0cb 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/result/ParameterValidationExceptionResultHandler.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/result/ParameterValidationExceptionResultHandler.java @@ -24,6 +24,7 @@ import java.util.stream.StreamSupport; import javax.validation.ElementKind; import javax.validation.Path; +import org.jline.terminal.Terminal; import org.jline.utils.AttributedString; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; @@ -43,6 +44,10 @@ import org.springframework.shell.Utils; public class ParameterValidationExceptionResultHandler extends TerminalAwareResultHandler { + public ParameterValidationExceptionResultHandler(Terminal terminal) { + super(terminal); + } + @Autowired private List parameterResolvers; diff --git a/spring-shell-core/src/main/java/org/springframework/shell/result/ResultHandlerConfig.java b/spring-shell-core/src/main/java/org/springframework/shell/result/ResultHandlerConfig.java index dcf77c37..666ac81a 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/result/ResultHandlerConfig.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/result/ResultHandlerConfig.java @@ -16,10 +16,15 @@ package org.springframework.shell.result; +import org.jline.terminal.Terminal; + +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.shell.CommandRegistry; import org.springframework.shell.TerminalSizeAware; +import org.springframework.shell.jline.InteractiveShellApplicationRunner; /** * Used for explicit configuration of {@link org.springframework.shell.ResultHandler}s. @@ -32,28 +37,29 @@ public class ResultHandlerConfig { @Bean @ConditionalOnClass(TerminalSizeAware.class) - public TerminalSizeAwareResultHandler terminalSizeAwareResultHandler() { - return new TerminalSizeAwareResultHandler(); + public TerminalSizeAwareResultHandler terminalSizeAwareResultHandler(Terminal terminal) { + return new TerminalSizeAwareResultHandler(terminal); } @Bean - public AttributedCharSequenceResultHandler attributedCharSequenceResultHandler() { - return new AttributedCharSequenceResultHandler(); + public AttributedCharSequenceResultHandler attributedCharSequenceResultHandler(Terminal terminal) { + return new AttributedCharSequenceResultHandler(terminal); } @Bean - public DefaultResultHandler defaultResultHandler() { - return new DefaultResultHandler(); + public DefaultResultHandler defaultResultHandler(Terminal terminal) { + return new DefaultResultHandler(terminal); } @Bean - public ParameterValidationExceptionResultHandler parameterValidationExceptionResultHandler() { - return new ParameterValidationExceptionResultHandler(); + public ParameterValidationExceptionResultHandler parameterValidationExceptionResultHandler(Terminal terminal) { + return new ParameterValidationExceptionResultHandler(terminal); } @Bean - public ThrowableResultHandler throwableResultHandler() { - return new ThrowableResultHandler(); + public ThrowableResultHandler throwableResultHandler(Terminal terminal, CommandRegistry commandRegistry, + ObjectProvider interactiveApplicationRunner) { + return new ThrowableResultHandler(terminal, commandRegistry, interactiveApplicationRunner); } } diff --git a/spring-shell-core/src/main/java/org/springframework/shell/result/TerminalAwareResultHandler.java b/spring-shell-core/src/main/java/org/springframework/shell/result/TerminalAwareResultHandler.java index d28189c0..66553afb 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/result/TerminalAwareResultHandler.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/result/TerminalAwareResultHandler.java @@ -18,8 +18,6 @@ package org.springframework.shell.result; import org.jline.terminal.Terminal; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; import org.springframework.shell.ResultHandler; /** @@ -28,10 +26,10 @@ import org.springframework.shell.ResultHandler; * @author Eric Bottard */ public abstract class TerminalAwareResultHandler implements ResultHandler { + protected Terminal terminal; - @Autowired @Lazy - public void setTerminal(Terminal terminal) { + protected TerminalAwareResultHandler(Terminal terminal) { this.terminal = terminal; } diff --git a/spring-shell-core/src/main/java/org/springframework/shell/result/TerminalSizeAwareResultHandler.java b/spring-shell-core/src/main/java/org/springframework/shell/result/TerminalSizeAwareResultHandler.java index ddf77320..dc3bde81 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/result/TerminalSizeAwareResultHandler.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/result/TerminalSizeAwareResultHandler.java @@ -16,6 +16,8 @@ package org.springframework.shell.result; +import org.jline.terminal.Terminal; + import org.springframework.shell.TerminalSizeAware; /** @@ -25,6 +27,9 @@ import org.springframework.shell.TerminalSizeAware; */ public class TerminalSizeAwareResultHandler extends TerminalAwareResultHandler { + public TerminalSizeAwareResultHandler(Terminal terminal) { + super(terminal); + } @Override protected void doHandleResult(TerminalSizeAware result) { diff --git a/spring-shell-core/src/main/java/org/springframework/shell/result/ThrowableResultHandler.java b/spring-shell-core/src/main/java/org/springframework/shell/result/ThrowableResultHandler.java index 7fcda340..10f2ce6f 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/result/ThrowableResultHandler.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/result/ThrowableResultHandler.java @@ -16,12 +16,12 @@ package org.springframework.shell.result; +import org.jline.terminal.Terminal; import org.jline.utils.AttributedString; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.shell.CommandRegistry; import org.springframework.shell.ResultHandler; import org.springframework.shell.jline.InteractiveShellApplicationRunner; @@ -43,11 +43,16 @@ public class ThrowableResultHandler extends TerminalAwareResultHandler interactiveRunner; + + public ThrowableResultHandler(Terminal terminal, CommandRegistry commandRegistry, + ObjectProvider interactiveRunner) { + super(terminal); + this.commandRegistry = commandRegistry; + this.interactiveRunner = interactiveRunner; + } @Override protected void doHandleResult(Throwable result) { @@ -55,7 +60,7 @@ public class ThrowableResultHandler extends TerminalAwareResultHandler> JCOMMANDER_ANNOTATIONS = Arrays.asList(Parameter.class, DynamicParameter.class, ParametersDelegate.class); - private Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + private Validator validator = Utils.defaultValidator(); @Autowired(required = false) public void setValidatorFactory(ValidatorFactory validatorFactory) { diff --git a/spring-shell-samples/src/main/java/org/springframework/shell/samples/noautoconf/NoAutoConf.java b/spring-shell-samples/src/main/java/org/springframework/shell/samples/noautoconf/NoAutoConf.java index 2198371f..94aac506 100644 --- a/spring-shell-samples/src/main/java/org/springframework/shell/samples/noautoconf/NoAutoConf.java +++ b/spring-shell-samples/src/main/java/org/springframework/shell/samples/noautoconf/NoAutoConf.java @@ -22,13 +22,13 @@ import org.springframework.context.annotation.Import; import org.springframework.shell.boot.JCommanderParameterResolverAutoConfiguration; import org.springframework.shell.boot.JLineShellAutoConfiguration; import org.springframework.shell.boot.SpringShellAutoConfiguration; +import org.springframework.shell.boot.StandardAPIAutoConfiguration; import org.springframework.shell.boot.StandardCommandsAutoConfiguration; import org.springframework.shell.samples.jcommander.JCommanderCommands; import org.springframework.shell.samples.standard.Commands; import org.springframework.shell.samples.standard.DynamicCommands; import org.springframework.shell.samples.standard.TableCommands; import org.springframework.shell.standard.FileValueProvider; -import org.springframework.shell.standard.StandardAPIAutoConfiguration; /** * This class shows how to use the full extent of Spring Shell without relying on Boot auto configuration. 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 ac8ad329..336056a6 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 the original author or authors. + * Copyright 2017-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. @@ -16,11 +16,9 @@ package org.springframework.shell.standard.commands; -import org.jline.terminal.Terminal; import org.jline.utils.InfoCmp; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; +import org.springframework.shell.standard.AbstractShellComponent; import org.springframework.shell.standard.ShellComponent; import org.springframework.shell.standard.ShellMethod; @@ -28,9 +26,10 @@ import org.springframework.shell.standard.ShellMethod; * ANSI console related commands. * * @author Eric Bottard + * @author Janne Valkealahti */ @ShellComponent -public class Clear { +public class Clear extends AbstractShellComponent { /** * Marker interface for beans providing {@literal clear} functionality to the shell. @@ -45,11 +44,11 @@ public class Clear { */ public interface Command {} - @Autowired @Lazy - private Terminal terminal; + public Clear() { + } @ShellMethod("Clear the shell screen.") public void clear() { - terminal.puts(InfoCmp.Capability.clear_screen); + getTerminal().puts(InfoCmp.Capability.clear_screen); } } diff --git a/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Help.java b/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Help.java index 773f3999..25e26a31 100644 --- a/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Help.java +++ b/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Help.java @@ -29,21 +29,18 @@ import java.util.TreeSet; import java.util.stream.Collectors; import javax.validation.MessageInterpolator; -import javax.validation.Validation; import javax.validation.ValidatorFactory; import javax.validation.metadata.ConstraintDescriptor; import org.jline.utils.AttributedStringBuilder; import org.jline.utils.AttributedStyle; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.shell.Availability; -import org.springframework.shell.CommandRegistry; import org.springframework.shell.MethodTarget; import org.springframework.shell.ParameterDescription; -import org.springframework.shell.ParameterResolver; import org.springframework.shell.Utils; +import org.springframework.shell.standard.AbstractShellComponent; import org.springframework.shell.standard.CommandValueProvider; import org.springframework.shell.standard.ShellComponent; import org.springframework.shell.standard.ShellMethod; @@ -60,7 +57,7 @@ import static java.util.stream.Collectors.toMap; * @author Eric Bottard */ @ShellComponent -public class Help { +public class Help extends AbstractShellComponent { /** * Marker interface for beans providing {@literal help} functionality to the shell. @@ -80,21 +77,9 @@ public class Help { public interface Command { } - private final List parameterResolvers; + private MessageInterpolator messageInterpolator = Utils.defaultValidatorFactory().getMessageInterpolator(); - private ObjectProvider commandRegistry; - - private MessageInterpolator messageInterpolator = Validation.buildDefaultValidatorFactory() - .getMessageInterpolator(); - - @Autowired - public Help(List parameterResolvers) { - this.parameterResolvers = parameterResolvers; - } - - @Autowired // ctor injection impossible b/c of circular dependency - public void setCommandRegistry(ObjectProvider commandRegistry) { - this.commandRegistry = commandRegistry; + public Help() { } @Autowired(required = false) @@ -102,7 +87,6 @@ public class Help { this.messageInterpolator = validatorFactory.getMessageInterpolator(); } - @ShellMethod(value = "Display help about available commands.", prefix = "-") public CharSequence help( @ShellOption(defaultValue = ShellOption.NULL, valueProvider = CommandValueProvider.class, value = { "-C", @@ -121,7 +105,7 @@ public class Help { * Return a description of a specific command. Uses a layout inspired by *nix man pages. */ private CharSequence documentCommand(String command) { - MethodTarget methodTarget = commandRegistry.getIfAvailable().listCommands().get(command); + MethodTarget methodTarget = getCommandRegistry().listCommands().get(command); if (methodTarget == null) { throw new IllegalArgumentException("Unknown command '" + command + "'"); } @@ -248,7 +232,7 @@ public class Help { } private void documentAliases(AttributedStringBuilder result, String command, MethodTarget methodTarget) { - Set aliases = commandRegistry.getIfAvailable().listCommands().entrySet().stream() + Set aliases = getCommandRegistry().listCommands().entrySet().stream() .filter(e -> e.getValue().equals(methodTarget)) .map(Map.Entry::getKey) .filter(c -> !command.equals(c)) @@ -277,7 +261,7 @@ public class Help { } private CharSequence listCommands() { - Map commandsByName = commandRegistry.getIfAvailable().listCommands(); + Map commandsByName = getCommandRegistry().listCommands(); SortedMap> commandsByGroupAndName = commandsByName.entrySet().stream() .collect(groupingBy(e -> e.getValue().getGroup(), TreeMap::new, // group by and sort by command group @@ -335,7 +319,7 @@ public class Help { private List getParameterDescriptions(MethodTarget methodTarget) { return Utils.createMethodParameters(methodTarget.getMethod()) - .flatMap(mp -> parameterResolvers.stream().filter(pr -> pr.supports(mp)).limit(1L) + .flatMap(mp -> getParameterResolver().filter(pr -> pr.supports(mp)).limit(1L) .flatMap(pr -> pr.describe(mp))) .collect(Collectors.toList()); diff --git a/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Script.java b/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Script.java index 0b31e60d..c0d855b3 100644 --- a/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Script.java +++ b/spring-shell-standard-commands/src/main/java/org/springframework/shell/standard/commands/Script.java @@ -1,3 +1,18 @@ +/* + * Copyright 2017-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.standard.commands; import java.io.File; @@ -7,9 +22,8 @@ import java.io.Reader; import org.jline.reader.Parser; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.shell.Shell; import org.springframework.shell.jline.FileInputProvider; +import org.springframework.shell.standard.AbstractShellComponent; import org.springframework.shell.standard.ShellComponent; import org.springframework.shell.standard.ShellMethod; @@ -17,16 +31,14 @@ import org.springframework.shell.standard.ShellMethod; * A command that can read and execute other commands from a file. * * @author Eric Bottard + * @author Janne Valkealahti */ @ShellComponent -public class Script { - - private final ObjectProvider shell; +public class Script extends AbstractShellComponent { private final Parser parser; - public Script(ObjectProvider shell, Parser parser) { - this.shell = shell; + public Script(Parser parser) { this.parser = parser; } @@ -48,7 +60,7 @@ public class Script { public void script(File file) throws IOException { Reader reader = new FileReader(file); try (FileInputProvider inputProvider = new FileInputProvider(reader, parser)) { - shell.getIfAvailable().run(inputProvider); + getShell().run(inputProvider); } } 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 ea4c6679..d7badd29 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 the original author or authors. + * Copyright 2017-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. @@ -13,22 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.shell.standard.commands; -import org.jline.terminal.Terminal; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.shell.result.ThrowableResultHandler; +import org.springframework.shell.standard.AbstractShellComponent; import org.springframework.shell.standard.ShellComponent; import org.springframework.shell.standard.ShellMethod; /** * A command to display the full stacktrace when an error occurs. + * + * @author Eric Bottard + * @author Janne Valkealahti */ @ShellComponent -public class Stacktrace { +public class Stacktrace extends AbstractShellComponent { /** * Marker interface for beans providing {@literal stacktrace} functionality to the shell. @@ -43,17 +43,16 @@ public class Stacktrace { */ public interface Command {} - @Autowired @Lazy - private Terminal terminal; - - @Autowired - private ThrowableResultHandler throwableResultHandler; + private ObjectProvider throwableResultHandler; + public Stacktrace(ObjectProvider throwableResultHandler) { + this.throwableResultHandler = throwableResultHandler; + } @ShellMethod(key = ThrowableResultHandler.DETAILS_COMMAND_NAME, value = "Display the full stacktrace of the last error.") public void stacktrace() { - if (throwableResultHandler.getLastError() != null) { - throwableResultHandler.getLastError().printStackTrace(terminal.writer()); + if (throwableResultHandler.getIfAvailable().getLastError() != null) { + throwableResultHandler.getIfAvailable().getLastError().printStackTrace(getTerminal().writer()); } } } diff --git a/spring-shell-standard-commands/src/test/java/org/springframework/shell/standard/commands/HelpTest.java b/spring-shell-standard-commands/src/test/java/org/springframework/shell/standard/commands/HelpTest.java index 475037bb..cc5d6224 100644 --- a/spring-shell-standard-commands/src/test/java/org/springframework/shell/standard/commands/HelpTest.java +++ b/spring-shell-standard-commands/src/test/java/org/springframework/shell/standard/commands/HelpTest.java @@ -117,8 +117,8 @@ public class HelpTest { static class Config { @Bean - public Help help() { - return new Help(Collections.singletonList(parameterResolver())); + public Help help(CommandRegistry commandRegistry) { + return new Help(); } @Bean @@ -148,7 +148,7 @@ public class HelpTest { @Bean public ParameterResolver parameterResolver() { - return new StandardParameterResolver(new DefaultConversionService()); + return new StandardParameterResolver(new DefaultConversionService(), Collections.emptySet()); } @Bean diff --git a/spring-shell-standard/src/main/java/org/springframework/shell/standard/AbstractShellComponent.java b/spring-shell-standard/src/main/java/org/springframework/shell/standard/AbstractShellComponent.java new file mode 100644 index 00000000..0aeadb46 --- /dev/null +++ b/spring-shell-standard/src/main/java/org/springframework/shell/standard/AbstractShellComponent.java @@ -0,0 +1,76 @@ +/* + * 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.standard; + +import java.util.stream.Stream; + +import org.jline.terminal.Terminal; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.shell.CommandRegistry; +import org.springframework.shell.ParameterResolver; +import org.springframework.shell.Shell; + +/** + * Base class helping to build shell components. + * + * @author Janne Valkealahti + */ +public class AbstractShellComponent implements ApplicationContextAware, InitializingBean { + + private ApplicationContext applicationContext; + + private ObjectProvider shellProvider; + + private ObjectProvider terminalProvider; + + private ObjectProvider commandRegistryProvider; + + private ObjectProvider parameterResolverProvider; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public void afterPropertiesSet() throws Exception { + shellProvider = applicationContext.getBeanProvider(Shell.class); + terminalProvider = applicationContext.getBeanProvider(Terminal.class); + commandRegistryProvider = applicationContext.getBeanProvider(CommandRegistry.class); + parameterResolverProvider = applicationContext.getBeanProvider(ParameterResolver.class); + } + + protected Shell getShell() { + return shellProvider.getObject(); + } + + protected Terminal getTerminal() { + return terminalProvider.getObject(); + } + + protected CommandRegistry getCommandRegistry() { + return commandRegistryProvider.getObject(); + } + + protected Stream getParameterResolver() { + return parameterResolverProvider.orderedStream(); + } +} 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 55061007..683e43b6 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 @@ -16,21 +16,32 @@ package org.springframework.shell.standard; -import static org.springframework.util.StringUtils.collectionToDelimitedString; - import java.lang.reflect.Method; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.shell.*; +import org.springframework.shell.Availability; +import org.springframework.shell.Command; +import org.springframework.shell.ConfigurableCommandRegistry; +import org.springframework.shell.MethodTarget; +import org.springframework.shell.MethodTargetRegistrar; +import org.springframework.shell.Utils; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; +import static org.springframework.util.StringUtils.collectionToDelimitedString; + /** * The standard implementation of {@link MethodTargetRegistrar} for new shell * applications, resolves methods annotated with {@link ShellMethod} on @@ -40,13 +51,13 @@ import org.springframework.util.StringUtils; * @author Florent Biville * @author Camilo Gonzalez */ -public class StandardMethodTargetRegistrar implements MethodTargetRegistrar { +public class StandardMethodTargetRegistrar implements MethodTargetRegistrar, ApplicationContextAware { private ApplicationContext applicationContext; private Map commands = new HashMap<>(); - @Autowired + @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } diff --git a/spring-shell-standard/src/main/java/org/springframework/shell/standard/StandardParameterResolver.java b/spring-shell-standard/src/main/java/org/springframework/shell/standard/StandardParameterResolver.java index b50acab4..05183351 100644 --- a/spring-shell-standard/src/main/java/org/springframework/shell/standard/StandardParameterResolver.java +++ b/spring-shell-standard/src/main/java/org/springframework/shell/standard/StandardParameterResolver.java @@ -35,7 +35,6 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import javax.validation.metadata.MethodDescriptor; @@ -97,6 +96,8 @@ public class StandardParameterResolver implements ParameterResolver { private Collection valueProviders = new HashSet<>(); + private Validator validator = Utils.defaultValidator(); + /** * A cache from method+input to String representation of actual parameter values. Note * that the converted result is not cached, to allow dynamic computation to happen at @@ -104,24 +105,16 @@ public class StandardParameterResolver implements ParameterResolver { */ private final Map> parameterCache = new ConcurrentReferenceHashMap<>(); - @Autowired - public StandardParameterResolver(ConversionService conversionService) { + public StandardParameterResolver(ConversionService conversionService, Set valueProviders) { this.conversionService = conversionService; - } - - @Autowired(required = false) - public void setValueProviders(Collection valueProviders) { this.valueProviders = valueProviders; } - private Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); - @Autowired(required = false) public void setValidatorFactory(ValidatorFactory validatorFactory) { this.validator = validatorFactory.getValidator(); } - @Override public boolean supports(MethodParameter parameter) { boolean optOut = parameter.hasParameterAnnotation(ShellOption.class) diff --git a/spring-shell-standard/src/main/resources/META-INF/spring.factories b/spring-shell-standard/src/main/resources/META-INF/spring.factories deleted file mode 100644 index a575b3ed..00000000 --- a/spring-shell-standard/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,2 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -org.springframework.shell.standard.StandardAPIAutoConfiguration diff --git a/spring-shell-standard/src/test/java/org/springframework/shell/standard/StandardParameterResolverTest.java b/spring-shell-standard/src/test/java/org/springframework/shell/standard/StandardParameterResolverTest.java index 512474a9..834cba2d 100644 --- a/spring-shell-standard/src/test/java/org/springframework/shell/standard/StandardParameterResolverTest.java +++ b/spring-shell-standard/src/test/java/org/springframework/shell/standard/StandardParameterResolverTest.java @@ -17,7 +17,10 @@ package org.springframework.shell.standard; import java.lang.reflect.Method; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import org.jline.reader.ParsedLine; @@ -33,7 +36,6 @@ import org.springframework.shell.Utils; import org.springframework.shell.ValueResult; import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.springframework.shell.ValueResultAsserts.assertThat; @@ -46,12 +48,14 @@ import static org.springframework.util.ReflectionUtils.findMethod; */ public class StandardParameterResolverTest { - private StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService()); + // private StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(), Collections.emptySet()); // Tests for resolution @Test public void testParses() throws Exception { + StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(), + Collections.emptySet()); Method method = findMethod(Remote.class, "zap", boolean.class, String.class, String.class, String.class); List words = asList("--force --name --foo y".split(" ")); @@ -74,6 +78,8 @@ public class StandardParameterResolverTest { @Test public void testParsesWithMethodPrefix() throws Exception { + StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(), + Collections.emptySet()); Method method = findMethod(Remote.class, "prefixTest", String.class); ValueResult result = resolver.resolve(Utils.createMethodParameter(method, 0), @@ -83,6 +89,8 @@ public class StandardParameterResolverTest { @Test public void testParameterSpecifiedTwiceViaDifferentAliases() throws Exception { + StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(), + Collections.emptySet()); Method method = findMethod(Remote.class, "zap", boolean.class, String.class, String.class, String.class); assertThatThrownBy(() -> { @@ -95,6 +103,8 @@ public class StandardParameterResolverTest { @Test public void testParameterSpecifiedTwiceViaSameKey() throws Exception { + StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(), + Collections.emptySet()); Method method = findMethod(Remote.class, "zap", boolean.class, String.class, String.class, String.class); assertThatThrownBy(() -> { @@ -107,6 +117,8 @@ public class StandardParameterResolverTest { @Test public void testTooMuchInput() throws Exception { + StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(), + Collections.emptySet()); Method method = findMethod(Remote.class, "zap", boolean.class, String.class, String.class, String.class); assertThatThrownBy(() -> { @@ -119,6 +131,8 @@ public class StandardParameterResolverTest { @Test public void testIncompleteCommandResolution() throws Exception { + StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(), + Collections.emptySet()); Method method = findMethod(Remote.class, "shutdown", Remote.Delay.class); assertThatThrownBy(() -> { @@ -131,6 +145,8 @@ public class StandardParameterResolverTest { @Test public void testIncompleteCommandResolutionBigArity() throws Exception { + StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(), + Collections.emptySet()); Method method = findMethod(Remote.class, "add", List.class); assertThatThrownBy(() -> { @@ -143,6 +159,8 @@ public class StandardParameterResolverTest { @Test public void testUnresolvableArg() throws Exception { + StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(), + Collections.emptySet()); Method method = findMethod(Remote.class, "zap", boolean.class, String.class, String.class, String.class); assertThatThrownBy(() -> { @@ -157,6 +175,8 @@ public class StandardParameterResolverTest { @Test public void testParameterKeyNotYetSetAppearsInProposals() { + StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(), + Collections.emptySet()); Method method = findMethod(Remote.class, "zap", boolean.class, String.class, String.class, String.class); List completions = resolver.complete( Utils.createMethodParameter(method, 1), @@ -172,6 +192,8 @@ public class StandardParameterResolverTest { @Test public void testParameterKeyNotFullySpecified() { + StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(), + Collections.emptySet()); Method method = findMethod(Remote.class, "zap", boolean.class, String.class, String.class, String.class); List completions = resolver.complete( Utils.createMethodParameter(method, 1), @@ -187,6 +209,8 @@ public class StandardParameterResolverTest { @Test public void testNoMoreAvailableParameters() { + StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(), + Collections.emptySet()); Method method = findMethod(Remote.class, "zap", boolean.class, String.class, String.class, String.class); List completions = resolver.complete( Utils.createMethodParameter(method, 2), // trying to complete --foo @@ -197,6 +221,8 @@ public class StandardParameterResolverTest { @Test public void testNotTheRightTimeToCompleteThatParameter() { + StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(), + Collections.emptySet()); Method method = findMethod(Remote.class, "shutdown", Remote.Delay.class); List completions = resolver.complete( Utils.createMethodParameter(method, 0), @@ -207,8 +233,10 @@ public class StandardParameterResolverTest { @Test public void testValueCompletionWithNonDefaultArity() { - - resolver.setValueProviders(singletonList(new Remote.NumberValueProvider("12", "42", "7"))); + Set valueProviders = new HashSet<>(); + valueProviders.add(new Remote.NumberValueProvider("12", "42", "7")); + StandardParameterResolver resolver = new StandardParameterResolver(new DefaultConversionService(), + valueProviders); Method[] methods = { findMethod(org.springframework.shell.standard.Remote.class, "add", List.class),