Skip to content

Commit 71960e5

Browse files
committed
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
1 parent aaa7d87 commit 71960e5

File tree

9 files changed

+208
-64
lines changed

9 files changed

+208
-64
lines changed

Diff for: spring-shell-core/src/main/java/org/springframework/shell/DefaultShellApplicationRunner.java

+22-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021-2022 the original author or authors.
2+
* Copyright 2021-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -56,6 +56,27 @@ public DefaultShellApplicationRunner(List<ShellRunner> shellRunners) {
5656
@Override
5757
public void run(ApplicationArguments args) throws Exception {
5858
log.debug("Checking shell runners {}", shellRunners);
59+
60+
// Handle new ShellRunner api
61+
String[] sourceArgs = args.getSourceArgs();
62+
boolean canRun = false;
63+
for (ShellRunner runner : shellRunners) {
64+
try {
65+
canRun = runner.run(sourceArgs);
66+
} catch (Exception e) {
67+
break;
68+
}
69+
if (canRun) {
70+
break;
71+
}
72+
}
73+
74+
if (canRun) {
75+
// new api handled execution
76+
return;
77+
}
78+
79+
// Handle old deprecated ShellRunner api
5980
Optional<ShellRunner> optional = shellRunners.stream()
6081
.filter(sh -> sh.canRun(args))
6182
.findFirst();

Diff for: spring-shell-core/src/main/java/org/springframework/shell/ShellRunner.java

+28-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 the original author or authors.
2+
* Copyright 2021-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,16 +27,41 @@ public interface ShellRunner {
2727
/**
2828
* Checks if a particular shell runner can execute.
2929
*
30+
* For {@link #canRun(ApplicationArguments)} and
31+
* {@link #run(ApplicationArguments)} prefer {@link #run(String[])}.
32+
*
3033
* @param args the application arguments
3134
* @return true if shell runner can execute
3235
*/
33-
boolean canRun(ApplicationArguments args);
36+
@Deprecated(since = "3.3.0", forRemoval = true)
37+
default boolean canRun(ApplicationArguments args) {
38+
return false;
39+
}
3440

3541
/**
3642
* Execute application.
3743
*
44+
* For {@link #canRun(ApplicationArguments)} and
45+
* {@link #run(ApplicationArguments)} prefer {@link #run(String[])}.
46+
*
3847
* @param args the application argumets
3948
* @throws Exception in errors
4049
*/
41-
void run(ApplicationArguments args) throws Exception;
50+
@Deprecated(since = "3.3.0", forRemoval = true)
51+
default void run(ApplicationArguments args) throws Exception {
52+
throw new UnsupportedOperationException("Should get implemented together with canRun");
53+
}
54+
55+
/**
56+
* Execute {@code ShellRunner} with given args. Return value indicates if run
57+
* operation happened and no further runners should be used.
58+
*
59+
* @param args the raw arguments
60+
* @return true if run execution happened
61+
* @throws Exception possible error during run
62+
*/
63+
default boolean run(String[] args) throws Exception {
64+
return false;
65+
}
66+
4267
}

Diff for: spring-shell-core/src/main/java/org/springframework/shell/jline/InteractiveShellRunner.java

+1-6
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import org.jline.reader.UserInterruptException;
2222
import org.jline.utils.AttributedString;
2323

24-
import org.springframework.boot.ApplicationArguments;
2524
import org.springframework.core.annotation.Order;
2625
import org.springframework.shell.ExitRequest;
2726
import org.springframework.shell.Input;
@@ -67,14 +66,10 @@ public InteractiveShellRunner(LineReader lineReader, PromptProvider promptProvid
6766
}
6867

6968
@Override
70-
public void run(ApplicationArguments args) throws Exception {
69+
public boolean run(String[] args) throws Exception {
7170
shellContext.setInteractionMode(InteractionMode.INTERACTIVE);
7271
InputProvider inputProvider = new JLineInputProvider(lineReader, promptProvider);
7372
shell.run(inputProvider);
74-
}
75-
76-
@Override
77-
public boolean canRun(ApplicationArguments args) {
7873
return true;
7974
}
8075

Diff for: spring-shell-core/src/main/java/org/springframework/shell/jline/NonInteractiveShellRunner.java

+15-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021-2023 the original author or authors.
2+
* Copyright 2021-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,7 +25,6 @@
2525
import org.jline.reader.Parser;
2626
import org.jline.reader.impl.DefaultParser;
2727

28-
import org.springframework.boot.ApplicationArguments;
2928
import org.springframework.core.annotation.Order;
3029
import org.springframework.shell.Input;
3130
import org.springframework.shell.InputProvider;
@@ -34,6 +33,7 @@
3433
import org.springframework.shell.Utils;
3534
import org.springframework.shell.context.InteractionMode;
3635
import org.springframework.shell.context.ShellContext;
36+
import org.springframework.util.ObjectUtils;
3737
import org.springframework.util.StringUtils;
3838

3939
/**
@@ -65,8 +65,8 @@ public class NonInteractiveShellRunner implements ShellRunner {
6565
private static final String SINGLE_QUOTE = "\'";
6666
private static final String DOUBLE_QUOTE = "\"";
6767

68-
private Function<ApplicationArguments, List<String>> commandsFromInputArgs = args -> {
69-
if (args.getSourceArgs().length == 0) {
68+
private Function<String[], List<String>> commandsFromArgs = args -> {
69+
if (ObjectUtils.isEmpty(args)) {
7070
if (StringUtils.hasText(primaryCommand)) {
7171
Collections.singletonList(primaryCommand);
7272
}
@@ -75,7 +75,7 @@ public class NonInteractiveShellRunner implements ShellRunner {
7575
}
7676
}
7777
// re-quote if needed having whitespace
78-
String raw = Arrays.stream(args.getSourceArgs())
78+
String raw = Arrays.stream(args)
7979
.map(a -> {
8080
if (!isQuoted(a) && StringUtils.containsWhitespace(a)) {
8181
return "\"" + a + "\"";
@@ -113,12 +113,12 @@ public NonInteractiveShellRunner(Shell shell, ShellContext shellContext, String
113113
/**
114114
* Sets the function that creates the command() to run from the input application arguments.
115115
*
116-
* @param commandsFromInputArgs function that takes input application arguments and creates zero or more commands
116+
* @param commandsFromArgs function that takes input application arguments and creates zero or more commands
117117
* where each command is a string that specifies the command and options
118118
* (eg. 'history --file myHistory.txt')
119119
*/
120-
public void setCommandsFromInputArgs(Function<ApplicationArguments, List<String>> commandsFromInputArgs) {
121-
this.commandsFromInputArgs = commandsFromInputArgs;
120+
public void setCommandsFromArgs(Function<String[], List<String>> commandsFromArgs) {
121+
this.commandsFromArgs = commandsFromArgs;
122122
}
123123

124124
/**
@@ -131,19 +131,18 @@ public void setLineParser(Parser lineParser) {
131131
}
132132

133133
@Override
134-
public boolean canRun(ApplicationArguments args) {
135-
return !commandsFromInputArgs.apply(args).isEmpty();
136-
}
137-
138-
@Override
139-
public void run(ApplicationArguments args) throws Exception {
140-
shellContext.setInteractionMode(InteractionMode.NONINTERACTIVE);
141-
List<String> commands = this.commandsFromInputArgs.apply(args);
134+
public boolean run(String[] args) throws Exception {
135+
List<String> commands = commandsFromArgs.apply(args);
136+
if (commands.isEmpty()) {
137+
return false;
138+
}
142139
List<ParsedLine> parsedLines = commands.stream()
143140
.map(rawCommandLine -> lineParser.parse(rawCommandLine, rawCommandLine.length() + 1))
144141
.collect(Collectors.toList());
145142
MultiParsedLineInputProvider inputProvider = new MultiParsedLineInputProvider(parsedLines);
143+
shellContext.setInteractionMode(InteractionMode.NONINTERACTIVE);
146144
shell.run(inputProvider);
145+
return true;
147146
}
148147

149148
/**

Diff for: spring-shell-core/src/main/java/org/springframework/shell/jline/ScriptShellRunner.java

+12-13
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,16 @@
1919
import java.io.File;
2020
import java.io.FileReader;
2121
import java.io.Reader;
22+
import java.util.Arrays;
2223
import java.util.List;
2324
import java.util.stream.Collectors;
2425

2526
import org.jline.reader.Parser;
2627

27-
import org.springframework.boot.ApplicationArguments;
2828
import org.springframework.core.annotation.Order;
2929
import org.springframework.shell.Shell;
3030
import org.springframework.shell.ShellRunner;
31+
import org.springframework.util.ObjectUtils;
3132

3233
/**
3334
* A {@link ShellRunner} that looks for process arguments that start with {@literal @}, which are then interpreted as
@@ -59,19 +60,16 @@ public ScriptShellRunner(Parser parser, Shell shell) {
5960
}
6061

6162
@Override
62-
public boolean canRun(ApplicationArguments args) {
63-
String[] sourceArgs = args.getSourceArgs();
64-
if (sourceArgs.length > 0 && sourceArgs[0].startsWith("@") && sourceArgs[0].length() > 1) {
65-
return true;
63+
public boolean run(String[] args) throws Exception {
64+
String[] sourceArgs = args;
65+
if (ObjectUtils.isEmpty(sourceArgs)) {
66+
return false;
67+
}
68+
if (!(sourceArgs[0].startsWith("@") && sourceArgs[0].length() > 1)) {
69+
return false;
6670
}
67-
return false;
68-
}
69-
70-
//tag::documentation[]
7171

72-
@Override
73-
public void run(ApplicationArguments args) throws Exception {
74-
List<File> scriptsToRun = args.getNonOptionArgs().stream()
72+
List<File> scriptsToRun = Arrays.asList(args).stream()
7573
.filter(s -> s.startsWith("@"))
7674
.map(s -> new File(s.substring(1)))
7775
.collect(Collectors.toList());
@@ -82,7 +80,8 @@ public void run(ApplicationArguments args) throws Exception {
8280
shell.run(inputProvider);
8381
}
8482
}
83+
84+
return true;
8585
}
86-
//end::documentation[]
8786

8887
}

Diff for: spring-shell-core/src/test/java/org/springframework/shell/jline/InteractiveShellRunnerTests.java

+37
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
* Copyright 2022-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package org.springframework.shell.jline;
217

318
import java.io.ByteArrayOutputStream;
@@ -14,6 +29,8 @@
1429
import org.jline.utils.AttributedStyle;
1530
import org.junit.jupiter.api.Test;
1631

32+
import org.springframework.boot.ApplicationArguments;
33+
import org.springframework.boot.DefaultApplicationArguments;
1734
import org.springframework.shell.ExitRequest;
1835

1936
import static org.assertj.core.api.Assertions.assertThat;
@@ -132,4 +149,24 @@ public void testExitWithCtrlD() throws Exception {
132149
readThread.join();
133150
writeThread.join();
134151
}
152+
153+
154+
@Test
155+
void oldApiCanRunReturnFalse() {
156+
InteractiveShellRunner runner = new InteractiveShellRunner(null, null, null, null);
157+
assertThat(runner.canRun(ofApplicationArguments())).isFalse();
158+
}
159+
160+
@Test
161+
void oldApiRunThrows() {
162+
InteractiveShellRunner runner = new InteractiveShellRunner(null, null, null, null);
163+
assertThatThrownBy(() -> {
164+
runner.run(ofApplicationArguments());
165+
});
166+
}
167+
168+
private static ApplicationArguments ofApplicationArguments(String... args) {
169+
return new DefaultApplicationArguments(args);
170+
}
171+
135172
}

0 commit comments

Comments
 (0)