diff --git a/README.adoc b/README.adoc index 20815057..8661815c 100644 --- a/README.adoc +++ b/README.adoc @@ -1,8 +1,7 @@ -= Spring Shell 2 += Spring Shell 3 -Spring Shell 2 is an ongoing effort to completely rewrite -https://github.com/spring-projects/spring-shell[Spring Shell] from scratch, making it more modular -while maintaining some level of backward compatibility with your existing commands. +Spring Shell 3 is a work to solely depend on Spring Boot 2.x and not trying to keep +any backward compatibility with older Spring Shell 1.x nor Spring Boot 1.x. == Building ``` diff --git a/pom.xml b/pom.xml index 1e97ea4e..3aa14910 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.8.RELEASE + 2.0.6.RELEASE @@ -26,7 +26,6 @@ 3.8.0 1.8 1.48 - 1.2.0.RELEASE @@ -35,7 +34,6 @@ spring-shell-standard spring-shell-standard-commands spring-shell-jcommander-adapter - spring-shell-shell1-adapter spring-shell-starter spring-shell-table spring-shell-docs @@ -64,11 +62,6 @@ spring-shell-standard-commands 3.0.0.BUILD-SNAPSHOT - - org.springframework.shell - spring-shell-shell1-adapter - 3.0.0.BUILD-SNAPSHOT - org.springframework.shell spring-shell-jcommander-adapter @@ -108,12 +101,6 @@ ${assertj.version} test - - - org.springframework.shell - spring-shell - ${shell1.version} - diff --git a/spring-shell-docs/pom.xml b/spring-shell-docs/pom.xml index d681a0ac..5b64b5d6 100644 --- a/spring-shell-docs/pom.xml +++ b/spring-shell-docs/pom.xml @@ -37,16 +37,6 @@ spring-shell-standard-commands provided - - org.springframework.shell - spring-shell-shell1-adapter - provided - - - org.springframework.shell - spring-shell - - org.springframework.shell spring-shell-jcommander-adapter 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 92fd571f..602526f4 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 @@ -17,14 +17,12 @@ package org.springframework.shell.samples.noautoconf; import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.shell.SpringShellAutoConfiguration; import org.springframework.shell.jcommander.JCommanderParameterResolverAutoConfiguration; import org.springframework.shell.jline.JLineShellAutoConfiguration; -import org.springframework.shell.legacy.LegacyAdapterAutoConfiguration; import org.springframework.shell.samples.jcommander.JCommanderCommands; import org.springframework.shell.samples.legacy.LegacyCommands; import org.springframework.shell.samples.standard.Commands; @@ -46,12 +44,11 @@ import org.springframework.shell.standard.commands.StandardCommandsAutoConfigura JLineShellAutoConfiguration.class, // Various Resolvers JCommanderParameterResolverAutoConfiguration.class, - LegacyAdapterAutoConfiguration.class, StandardAPIAutoConfiguration.class, // Built-In Commands StandardCommandsAutoConfiguration.class, // Allows ${} support - PropertyPlaceholderAutoConfiguration.class, + //PropertyPlaceholderAutoConfiguration.class, // Sample Commands JCommanderCommands.class, LegacyCommands.class, diff --git a/spring-shell-shell1-adapter/pom.xml b/spring-shell-shell1-adapter/pom.xml deleted file mode 100644 index 08b87e86..00000000 --- a/spring-shell-shell1-adapter/pom.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - 4.0.0 - - spring-shell-shell1-adapter - Spring Shell 1 Adapter - jar - - - org.springframework.shell - spring-shell-parent - 3.0.0.BUILD-SNAPSHOT - - - Adapter classes to enable Shell 1 type annotations via Spring Shell 2 - - - - org.springframework.shell - spring-shell-core - - - - - org.springframework.shell - spring-shell - true - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.shell - spring-shell-core-test-support - test - - - org.assertj - assertj-core - test - - - - - diff --git a/spring-shell-shell1-adapter/src/main/java/org/springframework/shell/legacy/LegacyAdapterAutoConfiguration.java b/spring-shell-shell1-adapter/src/main/java/org/springframework/shell/legacy/LegacyAdapterAutoConfiguration.java deleted file mode 100644 index e38b31c1..00000000 --- a/spring-shell-shell1-adapter/src/main/java/org/springframework/shell/legacy/LegacyAdapterAutoConfiguration.java +++ /dev/null @@ -1,62 +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.legacy; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.shell.converters.ArrayConverter; -import org.springframework.shell.converters.AvailableCommandsConverter; -import org.springframework.shell.converters.FileConverter; -import org.springframework.shell.converters.SimpleFileConverter; -import org.springframework.shell.core.annotation.CliCommand; - -import java.io.File; - -/** - * Main configuration class for the Shell 2 - Shell 1 adapter. - * - * @author Camilo Gonzalez - */ -@Configuration -@ConditionalOnClass(CliCommand.class) -@ComponentScan(basePackageClasses = {ArrayConverter.class}, excludeFilters = @ComponentScan.Filter( - type = FilterType.ASSIGNABLE_TYPE, - value = {AvailableCommandsConverter.class, SimpleFileConverter.class})) -public class LegacyAdapterAutoConfiguration { - - @Bean - public LegacyMethodTargetRegistrar legacyMethodTargetResolver() { - return new LegacyMethodTargetRegistrar(); - } - - @Bean - public LegacyParameterResolver legacyParameterResolver() { - return new LegacyParameterResolver(); - } - - @Bean - public FileConverter fileConverter() { - return new FileConverter() { - @Override - protected File getWorkingDirectory() { - return new File("."); - } - }; - } -} diff --git a/spring-shell-shell1-adapter/src/main/java/org/springframework/shell/legacy/LegacyMethodTargetRegistrar.java b/spring-shell-shell1-adapter/src/main/java/org/springframework/shell/legacy/LegacyMethodTargetRegistrar.java deleted file mode 100644 index fa153b63..00000000 --- a/spring-shell-shell1-adapter/src/main/java/org/springframework/shell/legacy/LegacyMethodTargetRegistrar.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2015-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.legacy; - -import static org.springframework.util.StringUtils.collectionToDelimitedString; - -import java.lang.reflect.Method; -import java.util.*; -import java.util.function.Supplier; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.shell.Availability; -import org.springframework.shell.ConfigurableCommandRegistry; -import org.springframework.shell.core.CommandMarker; -import org.springframework.shell.core.annotation.CliAvailabilityIndicator; -import org.springframework.shell.core.annotation.CliCommand; -import org.springframework.shell.MethodTarget; -import org.springframework.shell.MethodTargetRegistrar; -import org.springframework.stereotype.Component; -import org.springframework.util.ReflectionUtils; - -/** - * A {@link MethodTargetRegistrar} that discovers methods annotated with {@link CliCommand} on beans - * implementing the {@link CommandMarker} marker interface. - * - * @author Eric Bottard - * @author Florent Biville - * @author Camilo Gonzalez - */ -@Component -public class LegacyMethodTargetRegistrar implements MethodTargetRegistrar { - - @Autowired - private ApplicationContext applicationContext; - - private Map commands = new TreeMap<>(); - - @Override - public void register(ConfigurableCommandRegistry registry) { - Map beans = applicationContext.getBeansOfType(CommandMarker.class); - for (Object bean : beans.values()) { - Class clazz = bean.getClass(); - ReflectionUtils.doWithMethods(clazz, method -> { - CliCommand cliCommand = method.getAnnotation(CliCommand.class); - for (String key : cliCommand.value()) { - Supplier availabilityIndicator = bridgeAvailabilityIndicator(key, bean); - MethodTarget target = new MethodTarget(method, bean, cliCommand.help(), availabilityIndicator); - registry.register(key, target); - commands.put(key, target); - } - }, method -> method.getAnnotation(CliCommand.class) != null); - } - } - - private Supplier bridgeAvailabilityIndicator(String commandKey, Object bean) { - Class clazz = bean.getClass(); - Set candidates = new HashSet<>(); - - ReflectionUtils.doWithMethods(clazz, candidates::add, - method -> method.getAnnotation(CliAvailabilityIndicator.class) != null - && Arrays.asList(method.getAnnotation(CliAvailabilityIndicator.class).value()).contains(commandKey)); - - switch (candidates.size()) { - case 0: - return null; - case 1: - return () -> { - boolean available = (Boolean) ReflectionUtils.invokeMethod(candidates.iterator().next(), bean); - return available ? Availability.available() : Availability.unavailable("[Unknown reason]"); - }; - default: - throw new IllegalStateException("Looks like there are several @" + CliAvailabilityIndicator.class.getSimpleName() - + " for '" + commandKey + "'. Found " + candidates); - } - } - - @Override - public String toString() { - return getClass().getSimpleName() + " contributing " - + collectionToDelimitedString(commands.keySet(), ", ", "[", "]"); - } - -} diff --git a/spring-shell-shell1-adapter/src/main/java/org/springframework/shell/legacy/LegacyParameterResolver.java b/spring-shell-shell1-adapter/src/main/java/org/springframework/shell/legacy/LegacyParameterResolver.java deleted file mode 100644 index e3a8fc15..00000000 --- a/spring-shell-shell1-adapter/src/main/java/org/springframework/shell/legacy/LegacyParameterResolver.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright 2015-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.legacy; - -import java.lang.reflect.Parameter; -import java.util.*; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.MethodParameter; -import org.springframework.shell.core.Completion; -import org.springframework.shell.core.Converter; -import org.springframework.shell.core.MethodTarget; -import org.springframework.shell.core.annotation.CliOption; -import org.springframework.shell.CompletionContext; -import org.springframework.shell.CompletionProposal; -import org.springframework.shell.ParameterDescription; -import org.springframework.shell.ParameterResolver; -import org.springframework.shell.ValueResult; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; -import org.springframework.util.ReflectionUtils; - -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; -import javax.validation.metadata.MethodDescriptor; -import javax.validation.metadata.ParameterDescriptor; - -/** - * Resolves parameters by looking at the {@link CliOption} annotation and acting - * accordingly. - * - * @author Eric Bottard - * @author Camilo Gonzalez - */ -@Component -public class LegacyParameterResolver implements ParameterResolver { - - private static final String CLI_OPTION_NULL = "__NULL__"; - - /** - * Prefix used by Spring Shell 1 for the argument keys (e.g. command --key - * value). - */ - private static final String CLI_PREFIX = "--"; - - @Autowired(required = false) - private Collection> converters = new ArrayList<>(); - - private Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); - - @Autowired(required = false) - public void setValidatorFactory(ValidatorFactory validatorFactory) { - this.validator = validatorFactory.getValidator(); - } - - - @Override - public boolean supports(MethodParameter parameter) { - return parameter.hasParameterAnnotation(CliOption.class); - } - - @Override - public ValueResult resolve(MethodParameter methodParameter, List words) { - Optional> converter = findOptionalConverter(methodParameter); - CliOption cliOption = methodParameter.getParameterAnnotation(CliOption.class); - - Map values = parseOptions(words); - Map seenValues = convertValues(values, methodParameter, converter); - switch (seenValues.size()) { - case 0: - if (!cliOption.mandatory()) { - String value = cliOption.unspecifiedDefaultValue(); - Object resolvedValue = converter - .orElseThrow(noConverterFound(cliOption.key()[0], value, methodParameter.getParameterType())) - .convertFromText(value, methodParameter.getParameterType(), cliOption.optionContext()); - - return new ValueResult(methodParameter, resolvedValue); - } - else { - throw new IllegalArgumentException("Could not find parameter values for " - + prettifyKeys(Arrays.asList(cliOption.key())) + " in " + words); - } - case 1: - return seenValues.values().iterator().next(); - default: - throw new RuntimeException("Option has been set multiple times via " + prettifyKeys(seenValues.keySet())); - } - } - - /** - * Maybe find a Shell 1 Converter that applies to the given {@literal methodParameter}. - */ - private Optional> findOptionalConverter(MethodParameter methodParameter) { - CliOption cliOption = methodParameter.getParameterAnnotation(CliOption.class); - return converters.stream() - .filter(c -> c.supports(methodParameter.getParameterType(), cliOption.optionContext())) - .findFirst(); - } - - @Override - public Stream describe(MethodParameter parameter) { - Parameter jlrParameter = parameter.getMethod().getParameters()[parameter.getParameterIndex()]; - CliOption option = jlrParameter.getAnnotation(CliOption.class); - ParameterDescription result = ParameterDescription.outOf(parameter); - result.help(option.help()); - List keys = Arrays.asList(option.key()); - result.keys(notDefaultCommandKeys(parameter)); - if (!option.mandatory()) { - result.defaultValue(CLI_OPTION_NULL.equals(option.unspecifiedDefaultValue()) ? "null" - : option.unspecifiedDefaultValue()); - } - if (!CLI_OPTION_NULL.equals(option.specifiedDefaultValue())) { - result.whenFlag(option.specifiedDefaultValue()); - } - boolean containsEmptyKey = keys.contains(""); - result.mandatoryKey(!containsEmptyKey); - - MethodDescriptor constraintsForMethod = validator.getConstraintsForClass(parameter.getDeclaringClass()) - .getConstraintsForMethod(parameter.getMethod().getName(), parameter.getMethod().getParameterTypes()); - if (constraintsForMethod != null) { - ParameterDescriptor constraintsDescriptor = constraintsForMethod - .getParameterDescriptors().get(parameter.getParameterIndex()); - result.elementDescriptor(constraintsDescriptor); - } - - return Stream.of(result); - } - - /** - * Return the list of keys (with their "--" prefix) that can be used to set the given - * parameter. If the parameter supports the empty key, this is not part of the result. - */ - private List notDefaultCommandKeys(MethodParameter parameter) { - Parameter jlrParameter = parameter.getMethod().getParameters()[parameter.getParameterIndex()]; - CliOption option = jlrParameter.getAnnotation(CliOption.class); - return Arrays.stream(option.key()) - .filter(key -> !key.isEmpty()) - .map(key -> CLI_PREFIX + key) - .collect(Collectors.toList()); - } - - @Override - public List complete(MethodParameter parameter, CompletionContext context) { - String nextToLast = null; - String last; - if (context.getWords().size() >= 2) { - nextToLast = context.getWords().get(context.getWords().size() - 2); - } - if (context.getWords().size() >= 1) { - last = context.getWords().get(context.getWords().size() - 1); - } - else { - last = null; - } - List commandKeys = notDefaultCommandKeys(parameter); - if (nextToLast != null) { - if (commandKeys.contains(nextToLast)) { - // nextToLast is our key, last is our (possibly unfinished) value - if (findOptionalConverter(parameter).isPresent()) { - ArrayList legacyProposals = new ArrayList<>(); - findOptionalConverter(parameter).get().getAllPossibleValues( - legacyProposals, - parameter.getParameterType(), - last, - parameter.getParameterAnnotation(CliOption.class).optionContext(), - craftMethodTarget() - ); - return legacyProposals.stream() - .filter(lp -> lp.getValue().startsWith(last)) - .map(this::toCompletionProposal) - .collect(Collectors.toList()); - } else { - return Collections.emptyList(); - } - } // nextToLast looks like a key, but not for this parameter - else if (nextToLast.startsWith(CLI_PREFIX)) { - // Not for this parameter - return Collections.emptyList(); - } - } - // Fallthrough: nextToLast is the value to another parameter - // and last (possibly the empty string) could be our key - if (last != null) { - return commandKeys.stream() - .filter(k -> k.startsWith(last)) - .map(CompletionProposal::new) - .collect(Collectors.toList()); - } - // Invoked completion just after the command (without a space): my-command - return Collections.emptyList(); - } - - /** - * Turn a Shell 1 Completion into a CompletionProposal. - */ - private CompletionProposal toCompletionProposal(Completion c) { - return new CompletionProposal(c.getValue()) - .displayText(c.getFormattedValue()) - .category(c.getHeading()); - } - - // TODO pass invokable method in the completion context. Rarely used by converters, so ok for now - private MethodTarget craftMethodTarget() { - return new MethodTarget(ReflectionUtils.findMethod(Object.class, "toString"), "foo"); - } - - private Map parseOptions(List words) { - Map values = new HashMap<>(); - for (int i = 0; i < words.size(); i++) { - int from = i; - String word = words.get(i); - if (word.startsWith(CLI_PREFIX)) { - String key = word.substring(CLI_PREFIX.length()); - // If next word doesn't exist or starts with '--', this is an unary option. Store null - String value = i < words.size() - 1 && !words.get(i + 1).startsWith(CLI_PREFIX) ? words.get(++i) : null; - Assert.isTrue(!values.containsKey(key), - String.format("Option %s%s has already been set", CLI_PREFIX, key)); - values.put(key, new ParseResult(value, from)); - } // Must be the 'anonymous' option - else { - Assert.isTrue(!values.containsKey(""), "Anonymous option has already been set"); - values.put("", new ParseResult(word, from)); - } - } - return values; - } - - private Map convertValues(Map values, MethodParameter methodParameter, - Optional> converter) { - Map seenValues = new HashMap<>(); - CliOption option = methodParameter.getParameterAnnotation(CliOption.class); - for (String key : option.key()) { - if (values.containsKey(key)) { - ParseResult parseResult = values.get(key); - String value = parseResult.value; - if (value == null && !CLI_OPTION_NULL.equals(option.specifiedDefaultValue())) { - value = option.specifiedDefaultValue(); - } - Class parameterType = methodParameter.getParameterType(); - Object resolvedValue = converter - .orElseThrow(noConverterFound(key, value, parameterType)) - .convertFromText(value, parameterType, option.optionContext()); - int from = parseResult.from; - int to = key.isEmpty() || parseResult.value == null ? from : from + 1; - BitSet wordsUsed = new BitSet(); - wordsUsed.set(from, to + 1); - BitSet wordsUsedForValues = new BitSet(); - if (parseResult.value != null) { - wordsUsedForValues.set(to); - } - seenValues.put(key, new ValueResult(methodParameter, resolvedValue, wordsUsed, wordsUsedForValues)); - } - } - return seenValues; - } - - /** - * Return the list of possible keys for an option, suitable for displaying in an error - * message. - */ - private String prettifyKeys(Collection keys) { - return keys.stream().map(s -> "".equals(s) ? "" : CLI_PREFIX + s) - .collect(Collectors.joining(", ", "[", "]")); - } - - private Supplier noConverterFound(String key, String value, Class parameterType) { - return () -> new IllegalStateException( - "No converter found for " + CLI_PREFIX + key + " from '" + value + "' to type " + parameterType); - } - - private static class ParseResult { - private final String value; - - private final Integer from; - - public ParseResult(String value, Integer from) { - this.value = value; - this.from = from; - } - - } - -} diff --git a/spring-shell-shell1-adapter/src/main/java/org/springframework/shell/legacy/package-info.java b/spring-shell-shell1-adapter/src/main/java/org/springframework/shell/legacy/package-info.java deleted file mode 100644 index 32ce2628..00000000 --- a/spring-shell-shell1-adapter/src/main/java/org/springframework/shell/legacy/package-info.java +++ /dev/null @@ -1,20 +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. - */ - -/** - * Provides integration with Spring Shell 1. - */ -package org.springframework.shell.legacy; diff --git a/spring-shell-shell1-adapter/src/main/resources/META-INF/spring.factories b/spring-shell-shell1-adapter/src/main/resources/META-INF/spring.factories deleted file mode 100644 index a6006a67..00000000 --- a/spring-shell-shell1-adapter/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,2 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -org.springframework.shell.legacy.LegacyAdapterAutoConfiguration diff --git a/spring-shell-shell1-adapter/src/test/java/org/springframework/shell/legacy/ArtifactType.java b/spring-shell-shell1-adapter/src/test/java/org/springframework/shell/legacy/ArtifactType.java deleted file mode 100644 index aa9fb3f8..00000000 --- a/spring-shell-shell1-adapter/src/test/java/org/springframework/shell/legacy/ArtifactType.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2015 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.legacy; - -/** - * An example enum used for testing argument passing of Shell 1 commands. - * - * @author Eric Bottard - */ -public enum ArtifactType { - - source, processor, sink, task -} diff --git a/spring-shell-shell1-adapter/src/test/java/org/springframework/shell/legacy/LegacyCommands.java b/spring-shell-shell1-adapter/src/test/java/org/springframework/shell/legacy/LegacyCommands.java deleted file mode 100644 index c04cb276..00000000 --- a/spring-shell-shell1-adapter/src/test/java/org/springframework/shell/legacy/LegacyCommands.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2015 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.legacy; - -import java.lang.reflect.Method; - -import org.springframework.shell.core.CommandMarker; -import org.springframework.shell.core.annotation.CliCommand; -import org.springframework.shell.core.annotation.CliOption; -import org.springframework.util.ReflectionUtils; - -/** - * A set of Shell 1 commands used for tests. - * - * @author Eric Bottard - */ -public class LegacyCommands implements CommandMarker { - - public static final Method REGISTER_METHOD = ReflectionUtils.findMethod(LegacyCommands.class, "register", String.class, ArtifactType.class, String.class, boolean.class); - public static final Method SUM_METHOD = ReflectionUtils.findMethod(LegacyCommands.class, "sum", int.class, int.class); - public static final Method LEGACY_ECHO_METHOD = ReflectionUtils.findMethod(LegacyCommands.class, "legacyEcho", String.class); - public static final Method SOME_METHOD = ReflectionUtils.findMethod(LegacyCommands.class, "someMethod", String.class, boolean.class); - - @CliCommand(value = "register module", help = "Register a new module") - public String register( - @CliOption(mandatory = true, - key = {"", "name"}, - help = "the name for the registered module") - String name, - @CliOption(mandatory = true, - key = {"type"}, - help = "the type for the registered module") - ArtifactType type, - @CliOption(mandatory = true, - key = {"coordinates", "coords"}, - optionContext = "disable-string-converter", - help = "coordinates to the module archive") - String coordinates, - @CliOption(key = "force", - help = "force update if module already exists (only if not in use)", - specifiedDefaultValue = "true", - unspecifiedDefaultValue = "false") - boolean force) { - return String.format(("Successfully registered module '%s:%s'"), type, name); - } - - @CliCommand(value = "sum", help = "adds two numbers") - public int sum( - @CliOption(key = "v1", unspecifiedDefaultValue = "38") int a, - @CliOption(key = "v2", specifiedDefaultValue = "42") int b - ) { - return a + b; - } - - @CliCommand(value = "legacy-echo", help = "Echoes a message") - public String legacyEcho(@CliOption(key = "", mandatory = true) String message) { - return message; - } - - @CliCommand(value = "someMethod", help = "Method used for testing purposes") - public String someMethod( - @CliOption(key = "key", mandatory = false, help = "The optional parameter") String parameter, - @CliOption(key = "option", help = "an option", specifiedDefaultValue = "true", unspecifiedDefaultValue = "false") boolean option) { - return parameter + ", " + option; - } - -} diff --git a/spring-shell-shell1-adapter/src/test/java/org/springframework/shell/legacy/LegacyMethodTargetRegistrarTest.java b/spring-shell-shell1-adapter/src/test/java/org/springframework/shell/legacy/LegacyMethodTargetRegistrarTest.java deleted file mode 100644 index 391de8ac..00000000 --- a/spring-shell-shell1-adapter/src/test/java/org/springframework/shell/legacy/LegacyMethodTargetRegistrarTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2015 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.legacy; - - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.data.MapEntry.entry; - -import java.util.Map; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.shell.Command; -import org.springframework.shell.ConfigurableCommandRegistry; -import org.springframework.shell.MethodTarget; -import org.springframework.shell.MethodTargetRegistrar; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -/** - * Unit tests for {@link LegacyMethodTargetRegistrar}. - * - * @author Eric Bottard - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = LegacyMethodTargetRegistrarTest.Config.class) -public class LegacyMethodTargetRegistrarTest { - - @Autowired - private LegacyCommands legacyCommands; - - @Autowired - private MethodTargetRegistrar resolver; - - @Test - public void findsMethodsAnnotatedWithCliCommand() throws Exception { - ConfigurableCommandRegistry registry = new ConfigurableCommandRegistry(); - resolver.register(registry); - Map targets = registry.listCommands(); - - assertThat(targets).contains(entry( - "register module", - MethodTarget.of("register", legacyCommands, new Command.Help("Register a new module")) - )); - } - - @Configuration - static class Config { - - @Bean - public LegacyCommands legacyCommands() { - return new LegacyCommands(); - } - - @Bean - public MethodTargetRegistrar methodTargetResolver() { - return new LegacyMethodTargetRegistrar(); - } - } - -} diff --git a/spring-shell-shell1-adapter/src/test/java/org/springframework/shell/legacy/LegacyParameterResolverTest.java b/spring-shell-shell1-adapter/src/test/java/org/springframework/shell/legacy/LegacyParameterResolverTest.java deleted file mode 100644 index 122e3dee..00000000 --- a/spring-shell-shell1-adapter/src/test/java/org/springframework/shell/legacy/LegacyParameterResolverTest.java +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright 2015 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.legacy; - -import static java.util.Arrays.asList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.shell.ValueResultAsserts.assertThat; -import static org.springframework.shell.legacy.LegacyCommands.REGISTER_METHOD; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.MethodParameter; -import org.springframework.shell.*; -import org.springframework.shell.converters.BooleanConverter; -import org.springframework.shell.converters.EnumConverter; -import org.springframework.shell.converters.StringConverter; -import org.springframework.shell.core.Converter; -import org.springframework.shell.core.annotation.CliOption; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Tests for {@link LegacyParameterResolver}. - * - * @author Eric Bottard - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = LegacyParameterResolverTest.Config.class) -public class LegacyParameterResolverTest { - - private static final int NAME_OR_ANONYMOUS = 0; - private static final int TYPE = 1; - private static final int COORDINATES = 2; - private static final int FORCE = 3; - - @Autowired - ParameterResolver parameterResolver; - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Test - public void supportsParameterAnnotatedWithCliOption() throws Exception { - MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, NAME_OR_ANONYMOUS); - - boolean result = parameterResolver.supports(methodParameter); - - assertThat(result).isTrue(); - } - - @Test - public void resolvesParameterAnnotatedWithCliOption() throws Exception { - MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, NAME_OR_ANONYMOUS); - - assertThat(resolve(methodParameter, "--foo bar --name baz --qix bux")).hasValue("baz").usesWords(2, 3) - .usesWordsForValue(3); - } - - @Test - public void resolvesAnonymousParameterAnnotatedWithCliOption() throws Exception { - MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, NAME_OR_ANONYMOUS); - - assertThat(resolve(methodParameter, "--foo bar baz --qix bux")).hasValue("baz").usesWords(2) - .usesWordsForValue(2); - - assertThat(resolve(methodParameter, "baz --foo bar --qix bux")).hasValue("baz").usesWords(0) - .usesWordsForValue(0); - } - - @Test - public void usesLegacyConverters() throws Exception { - MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, TYPE); - - assertThat(resolve(methodParameter, "--foo bar --name baz --qix bux --type processor")) - .hasValue(ArtifactType.processor).usesWords(6, 7).usesWordsForValue(7); - } - - @Test - public void testUnspecifiedDefaultValue() throws Exception { - MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, FORCE); - - assertThat(resolve(methodParameter, "--foo bar --name baz --qix bux")) - .hasValue(false).notUsesWords().notUsesWordsForValue(); - } - - @Test - public void testSpecifiedDefaultValue() throws Exception { - MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, FORCE); - - assertThat(resolve(methodParameter, "--force --foo bar --name baz --qix bux")) - .hasValue(true).usesWords(0).notUsesWordsForValue(); - - assertThat(resolve(methodParameter, "--foo bar --name baz --qix bux --force")) - .hasValue(true).usesWords(6).notUsesWordsForValue(); - } - - @Test - public void testParameterNotFound() throws Exception { - MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, COORDINATES); - - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage( - "Could not find parameter values for [--coordinates, --coords] in [--force, --foo, bar, --name, baz, --qix, bux]"); - resolve(methodParameter, "--force --foo bar --name baz --qix bux"); - } - - @Test - public void testParameterFoundWithSameNameTooManyTimes() throws Exception { - MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, COORDINATES); - - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("Option --coordinates has already been set"); - resolve(methodParameter, "--force --coordinates bar --coordinates baz --qix bux"); - } - - @Test - public void testNoConverterFound() throws Exception { - MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SUM_METHOD, 0); - - thrown.expect(IllegalStateException.class); - thrown.expectMessage("No converter found for --v1 from '1' to type int"); - resolve(methodParameter, "--v1 1 --v2 2"); - } - - @Test - public void testNoConverterFoundForUnspecifiedValue() throws Exception { - MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SUM_METHOD, 0); - - thrown.expect(IllegalStateException.class); - thrown.expectMessage("No converter found for --v1 from '38' to type int"); - resolve(methodParameter, "--v2 2"); - } - - @Test - public void testNoConverterFoundForSpecifiedValue() throws Exception { - MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SUM_METHOD, 1); - - thrown.expect(IllegalStateException.class); - thrown.expectMessage("No converter found for --v2 from '42' to type int"); - resolve(methodParameter, "--v1 1 --v2"); - } - - @Test - public void testDescribeBothDefaultsNotDeclared() { - MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.REGISTER_METHOD, 1); - - ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get(); - - assertThat(description.keys()).containsExactly("--type"); - assertThat(description.formal()).isEqualTo(Utils.unCamelify(ArtifactType.class.getSimpleName())); - assertThat(description.defaultValue().isPresent()).isFalse(); - assertThat(description.mandatoryKey()).isTrue(); - - String expectedHelp = methodParameter.getParameterAnnotation(CliOption.class).help(); - assertThat(description.help()).isEqualTo(expectedHelp); - } - - @Test - public void testDescribeBothDefaultsDeclared() { - MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SOME_METHOD, 1); - - ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get(); - - assertThat(description.keys()).containsExactly("--option"); - assertThat(description.formal()).isEqualTo(boolean.class.getName()); - assertThat(description.defaultValue().get()).isEqualTo("false"); - assertThat(description.defaultValueWhenFlag().get()).isEqualTo("true"); - assertThat(description.mandatoryKey()).isTrue(); - - String expectedHelp = methodParameter.getParameterAnnotation(CliOption.class).help(); - assertThat(description.help()).isEqualTo(expectedHelp); - } - - @Test - public void testDescribeOnlySpecifiedDefaultDeclared() { - MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SUM_METHOD, 1); - - ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get(); - - assertThat(description.keys()).containsExactly("--v2"); - assertThat(description.formal()).isEqualTo(int.class.getName()); - assertThat(description.defaultValue().get()).isEqualTo("null"); - assertThat(description.defaultValueWhenFlag().get()).isEqualTo("42"); - assertThat(description.mandatoryKey()).isTrue(); - - String expectedHelp = methodParameter.getParameterAnnotation(CliOption.class).help(); - assertThat(description.help()).isEqualTo(expectedHelp); - } - - @Test - public void testDescribeOnlyUnspecifiedDefaultDeclared() { - MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SUM_METHOD, 0); - - ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get(); - - assertThat(description.keys()).containsExactly("--v1"); - assertThat(description.formal()).isEqualTo(int.class.getName()); - assertThat(description.defaultValue().get()).isEqualTo("38"); - assertThat(description.mandatoryKey()).isTrue(); - - String expectedHelp = methodParameter.getParameterAnnotation(CliOption.class).help(); - assertThat(description.help()).isEqualTo(expectedHelp); - } - - @Test - public void testDescribeDefaultKey() { - MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.LEGACY_ECHO_METHOD, 0); - - ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get(); - - assertThat(description.keys()).isEmpty(); - assertThat(description.formal()).isEqualTo(Utils.unCamelify(String.class.getSimpleName())); - assertThat(description.defaultValue().isPresent()).isFalse(); - assertThat(description.mandatoryKey()).isFalse(); - - String expectedHelp = methodParameter.getParameterAnnotation(CliOption.class).help(); - assertThat(description.help()).isEqualTo(expectedHelp); - } - - @Test - public void testDescribeNonMandatoryNoDefaults() { - MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SOME_METHOD, 0); - - ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get(); - - assertThat(description.keys()).containsExactly("--key"); - assertThat(description.formal()).isEqualTo(Utils.unCamelify(String.class.getSimpleName())); - assertThat(description.defaultValue().get()).isEqualTo("null"); - assertThat(description.mandatoryKey()).isTrue(); - - String expectedHelp = methodParameter.getParameterAnnotation(CliOption.class).help(); - assertThat(description.help()).isEqualTo(expectedHelp); - } - - private ValueResult resolve(MethodParameter methodParameter, String command) { - ValueResult result = parameterResolver.resolve(methodParameter, asList(command.split(" "))); - return result; - } - - // ======================== Completion Tests ========================== - - @Test - public void testNoCompletionJustAfterCommandWithNoSpace() { - MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, NAME_OR_ANONYMOUS); - - List proposals = parameterResolver.complete(methodParameter, new CompletionContext(Collections.emptyList(), -1, 0)); - assertThat(proposals).isEmpty(); - } - - @Test - public void testAllCommandKeysJustAfterCommand() { - MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, COORDINATES); - - List proposals = parameterResolver.complete(methodParameter, new CompletionContext(Collections.singletonList(""), 0, 0)); - assertThat(valuesOf(proposals)).contains("--coords", "--coordinates"); - } - - @Test - public void testAllCommandKeysWhenStarted() { - MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, COORDINATES); - - List proposals = parameterResolver.complete(methodParameter, new CompletionContext(Collections.singletonList("--co"), 0, 4)); - assertThat(valuesOf(proposals)).contains("--coords", "--coordinates"); - - proposals = parameterResolver.complete(methodParameter, new CompletionContext(Arrays.asList("--name", "foo", "--co"), 2, 4)); - assertThat(valuesOf(proposals)).contains("--coords", "--coordinates"); - } - - @Test - public void testNoCompletionsWhenAnotherParameterDetected() { - MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, COORDINATES); - - List proposals = parameterResolver.complete(methodParameter, new CompletionContext(Arrays.asList("--name", ""), 1, 0)); - assertThat(valuesOf(proposals)).isEmpty(); - - proposals = parameterResolver.complete(methodParameter, new CompletionContext(Arrays.asList("--name", "foo"), 1, 3)); - assertThat(valuesOf(proposals)).isEmpty(); - } - - @Test - public void testValueCompletionsWhenConverterAvailable() { - MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, FORCE); - - List proposals = parameterResolver.complete(methodParameter, new CompletionContext(Arrays.asList("--force", ""), 1, 0)); - assertThat(valuesOf(proposals)).contains("true", "false"); - - proposals = parameterResolver.complete(methodParameter, new CompletionContext(Arrays.asList("--force", "fa"), 1, 2)); - assertThat(valuesOf(proposals)).contains("false").doesNotContain("true"); - - } - - @Test - public void testNoValueCompletionsWhenNoConverterAvailable() { - MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, COORDINATES); - - List proposals = parameterResolver.complete(methodParameter, new CompletionContext(Arrays.asList("--coords", ""), 1, 0)); - assertThat(valuesOf(proposals)).isEmpty(); - - proposals = parameterResolver.complete(methodParameter, new CompletionContext(Arrays.asList("--coords", "foo"), 1, 3)); - assertThat(valuesOf(proposals)).isEmpty(); - - } - - private List valuesOf(List proposals) { - return proposals.stream().map(CompletionProposal::value).collect(Collectors.toList()); - } - - @Configuration - static class Config { - - @Bean - public Converter stringConverter() { - return new StringConverter(); - } - - @Bean - public Converter booleanConverter() { - return new BooleanConverter(); - } - - @Bean - public Converter> enumConverter() { - return new EnumConverter(); - } - - @Bean - public ParameterResolver parameterResolver() { - return new LegacyParameterResolver(); - } - } -} diff --git a/spring-shell-starter/pom.xml b/spring-shell-starter/pom.xml index afbf9592..4f8c033b 100644 --- a/spring-shell-starter/pom.xml +++ b/spring-shell-starter/pom.xml @@ -27,10 +27,6 @@ org.springframework.shell spring-shell-standard-commands - - org.springframework.shell - spring-shell-shell1-adapter - org.springframework.shell spring-shell-jcommander-adapter