Use string array in ShellRunner

- In ShellRunner interface replace use of boot's ApplicationArguments
  into plain string array.
- Deprecate old methods with some fallbacks to give
  time for users to do updates.
- NonInteractiveShellRunner contains one breaking change due to its
  public api to set function doing conversion from ApplicationArguments
  which was potentially used via customizer hooks.
- Deprecations planned to get removed in 3.4.x.
- Fixes #1057
This commit is contained in:
Janne Valkealahti
2024-05-02 08:40:17 +01:00
parent aaa7d879dc
commit 71960e5415
9 changed files with 208 additions and 64 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2021-2022 the original author or authors.
* Copyright 2021-2024 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.
@@ -56,6 +56,27 @@ public class DefaultShellApplicationRunner implements ShellApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
log.debug("Checking shell runners {}", shellRunners);
// Handle new ShellRunner api
String[] sourceArgs = args.getSourceArgs();
boolean canRun = false;
for (ShellRunner runner : shellRunners) {
try {
canRun = runner.run(sourceArgs);
} catch (Exception e) {
break;
}
if (canRun) {
break;
}
}
if (canRun) {
// new api handled execution
return;
}
// Handle old deprecated ShellRunner api
Optional<ShellRunner> optional = shellRunners.stream()
.filter(sh -> sh.canRun(args))
.findFirst();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2021 the original author or authors.
* Copyright 2021-2024 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.
@@ -27,16 +27,41 @@ public interface ShellRunner {
/**
* Checks if a particular shell runner can execute.
*
* For {@link #canRun(ApplicationArguments)} and
* {@link #run(ApplicationArguments)} prefer {@link #run(String[])}.
*
* @param args the application arguments
* @return true if shell runner can execute
*/
boolean canRun(ApplicationArguments args);
@Deprecated(since = "3.3.0", forRemoval = true)
default boolean canRun(ApplicationArguments args) {
return false;
}
/**
* Execute application.
*
* For {@link #canRun(ApplicationArguments)} and
* {@link #run(ApplicationArguments)} prefer {@link #run(String[])}.
*
* @param args the application argumets
* @throws Exception in errors
*/
void run(ApplicationArguments args) throws Exception;
@Deprecated(since = "3.3.0", forRemoval = true)
default void run(ApplicationArguments args) throws Exception {
throw new UnsupportedOperationException("Should get implemented together with canRun");
}
/**
* Execute {@code ShellRunner} with given args. Return value indicates if run
* operation happened and no further runners should be used.
*
* @param args the raw arguments
* @return true if run execution happened
* @throws Exception possible error during run
*/
default boolean run(String[] args) throws Exception {
return false;
}
}

View File

@@ -21,7 +21,6 @@ import org.jline.reader.LineReader;
import org.jline.reader.UserInterruptException;
import org.jline.utils.AttributedString;
import org.springframework.boot.ApplicationArguments;
import org.springframework.core.annotation.Order;
import org.springframework.shell.ExitRequest;
import org.springframework.shell.Input;
@@ -67,14 +66,10 @@ public class InteractiveShellRunner implements ShellRunner {
}
@Override
public void run(ApplicationArguments args) throws Exception {
public boolean run(String[] args) throws Exception {
shellContext.setInteractionMode(InteractionMode.INTERACTIVE);
InputProvider inputProvider = new JLineInputProvider(lineReader, promptProvider);
shell.run(inputProvider);
}
@Override
public boolean canRun(ApplicationArguments args) {
return true;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023 the original author or authors.
* Copyright 2021-2024 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.
@@ -25,7 +25,6 @@ import org.jline.reader.ParsedLine;
import org.jline.reader.Parser;
import org.jline.reader.impl.DefaultParser;
import org.springframework.boot.ApplicationArguments;
import org.springframework.core.annotation.Order;
import org.springframework.shell.Input;
import org.springframework.shell.InputProvider;
@@ -34,6 +33,7 @@ import org.springframework.shell.ShellRunner;
import org.springframework.shell.Utils;
import org.springframework.shell.context.InteractionMode;
import org.springframework.shell.context.ShellContext;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
@@ -65,8 +65,8 @@ public class NonInteractiveShellRunner implements ShellRunner {
private static final String SINGLE_QUOTE = "\'";
private static final String DOUBLE_QUOTE = "\"";
private Function<ApplicationArguments, List<String>> commandsFromInputArgs = args -> {
if (args.getSourceArgs().length == 0) {
private Function<String[], List<String>> commandsFromArgs = args -> {
if (ObjectUtils.isEmpty(args)) {
if (StringUtils.hasText(primaryCommand)) {
Collections.singletonList(primaryCommand);
}
@@ -75,7 +75,7 @@ public class NonInteractiveShellRunner implements ShellRunner {
}
}
// re-quote if needed having whitespace
String raw = Arrays.stream(args.getSourceArgs())
String raw = Arrays.stream(args)
.map(a -> {
if (!isQuoted(a) && StringUtils.containsWhitespace(a)) {
return "\"" + a + "\"";
@@ -113,12 +113,12 @@ public class NonInteractiveShellRunner implements ShellRunner {
/**
* Sets the function that creates the command() to run from the input application arguments.
*
* @param commandsFromInputArgs function that takes input application arguments and creates zero or more commands
* @param commandsFromArgs function that takes input application arguments and creates zero or more commands
* where each command is a string that specifies the command and options
* (eg. 'history --file myHistory.txt')
*/
public void setCommandsFromInputArgs(Function<ApplicationArguments, List<String>> commandsFromInputArgs) {
this.commandsFromInputArgs = commandsFromInputArgs;
public void setCommandsFromArgs(Function<String[], List<String>> commandsFromArgs) {
this.commandsFromArgs = commandsFromArgs;
}
/**
@@ -131,19 +131,18 @@ public class NonInteractiveShellRunner implements ShellRunner {
}
@Override
public boolean canRun(ApplicationArguments args) {
return !commandsFromInputArgs.apply(args).isEmpty();
}
@Override
public void run(ApplicationArguments args) throws Exception {
shellContext.setInteractionMode(InteractionMode.NONINTERACTIVE);
List<String> commands = this.commandsFromInputArgs.apply(args);
public boolean run(String[] args) throws Exception {
List<String> commands = commandsFromArgs.apply(args);
if (commands.isEmpty()) {
return false;
}
List<ParsedLine> parsedLines = commands.stream()
.map(rawCommandLine -> lineParser.parse(rawCommandLine, rawCommandLine.length() + 1))
.collect(Collectors.toList());
MultiParsedLineInputProvider inputProvider = new MultiParsedLineInputProvider(parsedLines);
shellContext.setInteractionMode(InteractionMode.NONINTERACTIVE);
shell.run(inputProvider);
return true;
}
/**

View File

@@ -19,15 +19,16 @@ package org.springframework.shell.jline;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.jline.reader.Parser;
import org.springframework.boot.ApplicationArguments;
import org.springframework.core.annotation.Order;
import org.springframework.shell.Shell;
import org.springframework.shell.ShellRunner;
import org.springframework.util.ObjectUtils;
/**
* A {@link ShellRunner} that looks for process arguments that start with {@literal @}, which are then interpreted as
@@ -59,19 +60,16 @@ public class ScriptShellRunner implements ShellRunner {
}
@Override
public boolean canRun(ApplicationArguments args) {
String[] sourceArgs = args.getSourceArgs();
if (sourceArgs.length > 0 && sourceArgs[0].startsWith("@") && sourceArgs[0].length() > 1) {
return true;
public boolean run(String[] args) throws Exception {
String[] sourceArgs = args;
if (ObjectUtils.isEmpty(sourceArgs)) {
return false;
}
if (!(sourceArgs[0].startsWith("@") && sourceArgs[0].length() > 1)) {
return false;
}
return false;
}
//tag::documentation[]
@Override
public void run(ApplicationArguments args) throws Exception {
List<File> scriptsToRun = args.getNonOptionArgs().stream()
List<File> scriptsToRun = Arrays.asList(args).stream()
.filter(s -> s.startsWith("@"))
.map(s -> new File(s.substring(1)))
.collect(Collectors.toList());
@@ -82,7 +80,8 @@ public class ScriptShellRunner implements ShellRunner {
shell.run(inputProvider);
}
}
return true;
}
//end::documentation[]
}