Reputation: 7890
Using JDK 17, Spring-Boot 3.3.0 and Spring-Shell 3.3.0 I'm experiencing ajava.io.IOError: java.io.InterruptedIOException: Command interrupted
exception when the UI application is legitimately terminated by the user. It appears to be triggered when eventLoop.dispatch(ShellMessageBuilder.ofInterrupt())
is called.
Relevant code:
@ShellComponent
@Log4j2
@RequiredArgsConstructor
public class TerminalUICommands {
private final TerminalUIBuilder tuiBuilder;
@ShellMethod(key = {"app"})
public void app() {
final TerminalUI tui = this.tuiBuilder.build();
final EventLoop eventLoop = tui.getEventLoop();
eventLoop.onDestroy(eventLoop.keyEvents()
.doOnNext(m -> {
if (m.getPlainKey() == KeyEvent.Key.q && m.hasCtrl()) {
eventLoop.dispatch(ShellMessageBuilder.ofInterrupt());
}
})
.subscribe());
final BoxView mainView = this.createMainView(tui);
final AppView appView = new AppView(
mainView,
this.createMenuView(tui, eventLoop),
this.createStatusView(tui, eventLoop));
tui.configure(appView);
tui.setRoot(appView, true);
tui.setFocus(mainView);
tui.run();
}
private BoxView createMainView(final TerminalUI tui) {
final BoxView view = new BoxView();
tui.configure(view);
return view;
}
private MenuBarView createMenuView(final TerminalUI tui, final EventLoop eventLoop) {
final MenuBarView menuBarView = MenuBarView.of(
MenuBarView.MenuBarItem.of("File", MenuView.MenuItem.of("Quit", MenuView.MenuItemCheckStyle.NOCHECK,
() -> eventLoop.dispatch(ShellMessageBuilder.ofInterrupt()))
));
tui.configure(menuBarView);
return menuBarView;
}
private StatusBarView createStatusView(final TerminalUI tui, final EventLoop eventLoop) {
final StatusBarView statusBarView = new StatusBarView(List.of(
StatusBarView.StatusItem.of("CTRL-Q Quit", () -> eventLoop.dispatch(ShellMessageBuilder.ofInterrupt()))
));
tui.configure(statusBarView);
return statusBarView;
}
}
@EnableAsync
@SpringBootApplication
public class CustomShellApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(CustomShellApplication.class)
.web(WebApplicationType.NONE)
.run(args)
.close();
}
}
Stack-trace:
java.io.IOError: java.io.InterruptedIOException: Command interrupted
at org.jline.terminal.impl.AbstractPosixTerminal.getSize(AbstractPosixTerminal.java:65)
at org.springframework.shell.component.view.TerminalUI.display(TerminalUI.java:262)
at org.springframework.shell.component.view.TerminalUI.loop(TerminalUI.java:394)
at org.springframework.shell.component.view.TerminalUI.run(TerminalUI.java:121)
at com.XXXXXXXX.customshell.command.TerminalUICommands.app(TerminalUICommands.java:68)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.shell.command.invocation.InvocableShellMethod.doInvoke(InvocableShellMethod.java:306)
at org.springframework.shell.command.invocation.InvocableShellMethod.invoke(InvocableShellMethod.java:232)
at org.springframework.shell.command.CommandExecution$DefaultCommandExecution.evaluate(CommandExecution.java:230)
at org.springframework.shell.Shell.evaluate(Shell.java:248)
at org.springframework.shell.Shell.run(Shell.java:159)
at org.springframework.shell.jline.InteractiveShellRunner.run(InteractiveShellRunner.java:72)
at org.springframework.shell.DefaultShellApplicationRunner.run(DefaultShellApplicationRunner.java:66)
at org.springframework.boot.SpringApplication.lambda$callRunner$4(SpringApplication.java:786)
at org.springframework.util.function.ThrowingConsumer$1.acceptWithException(ThrowingConsumer.java:83)
at org.springframework.util.function.ThrowingConsumer.accept(ThrowingConsumer.java:60)
at org.springframework.util.function.ThrowingConsumer$1.accept(ThrowingConsumer.java:88)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:786)
at org.springframework.boot.SpringApplication.lambda$callRunners$3(SpringApplication.java:774)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
at java.base/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:774)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:342)
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:149)
at com.XXXXXXXX.customshell.CustomShellApplication.main(CustomShellApplication.java:16)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:91)
at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:53)
at org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:58)
Caused by: java.io.InterruptedIOException: Command interrupted
at org.jline.utils.ExecHelper.exec(ExecHelper.java:52)
at org.jline.terminal.impl.exec.ExecPty.doGetConfig(ExecPty.java:177)
at org.jline.terminal.impl.exec.ExecPty.getSize(ExecPty.java:171)
at org.jline.terminal.impl.AbstractPosixTerminal.getSize(AbstractPosixTerminal.java:63)
... 41 common frames omitted
Caused by: java.lang.InterruptedException: null
at java.base/java.lang.Object.wait(Native Method)
at java.base/java.lang.Object.wait(Object.java:338)
at java.base/java.lang.ProcessImpl.waitFor(ProcessImpl.java:434)
at org.jline.utils.ExecHelper.waitAndCapture(ExecHelper.java:72)
at org.jline.utils.ExecHelper.exec(ExecHelper.java:42)
... 44 common frames omitted
I also get the same behaviour if I extract the code from the Spring-Shell example here
Upvotes: 0
Views: 49