How to run a standalone/interactive Spring Boot CRaSH Shell application? How to run a standalone/interactive Spring Boot CRaSH Shell application? shell shell

How to run a standalone/interactive Spring Boot CRaSH Shell application?


I had the same issue, so i implemented the following code to attach to a running shell configured by boot.

I did this by copying some of the code from org.crsh.standalone.CRaSH which loads a stand alone shell.

import org.crsh.console.jline.JLineProcessor;import org.crsh.console.jline.Terminal;import org.crsh.console.jline.TerminalFactory;import org.crsh.console.jline.console.ConsoleReader;import org.crsh.plugin.PluginLifeCycle;import org.crsh.shell.Shell;import org.crsh.shell.ShellFactory;import org.crsh.util.InterruptHandler;import org.fusesource.jansi.AnsiConsole;import org.springframework.beans.factory.DisposableBean;import org.springframework.beans.factory.InitializingBean;import org.springframework.boot.CommandLineRunner;import java.io.*;public class InteractiveShellRunner implements CommandLineRunner, InitializingBean, DisposableBean {    final private PluginLifeCycle crshBootstrapBean;    private Shell shell;    private Terminal term;    public InteractiveShellRunner(PluginLifeCycle crshBootstrapBean) {        this.crshBootstrapBean = crshBootstrapBean;    }    @Override    public void afterPropertiesSet() throws Exception {        ShellFactory shellFactory = crshBootstrapBean.getContext().getPlugin(ShellFactory.class);        shell = shellFactory.create(null);    }    @Override    public void destroy() throws Exception {        try {            if (term != null) {                term.restore();            }        } catch (Exception ignore) {        }    }    @Override    public void run(String... args) throws Exception {        if (shell != null) {            term = TerminalFactory.create();            //            String encoding = jline.internal.Configuration.getEncoding();            // Use AnsiConsole only if term doesnt support Ansi            PrintStream out;            PrintStream err;            boolean ansi;            if (term.isAnsiSupported()) {                out = new PrintStream(new BufferedOutputStream(term.wrapOutIfNeeded(new FileOutputStream(FileDescriptor.out)), 16384), false, encoding);                err = new PrintStream(new BufferedOutputStream(term.wrapOutIfNeeded(new FileOutputStream(FileDescriptor.err)), 16384), false, encoding);                ansi = true;            } else {                out = AnsiConsole.out;                err = AnsiConsole.err;                ansi = false;            }            //            FileInputStream in = new FileInputStream(FileDescriptor.in);            ConsoleReader reader = new ConsoleReader(null, in, out, term);            //            final JLineProcessor processor = new JLineProcessor(ansi, shell, reader, out);            //            InterruptHandler interruptHandler = new InterruptHandler(processor::interrupt);            interruptHandler.install();            //            Thread thread = new Thread(processor);            thread.setDaemon(true);            thread.start();            try {                processor.closed();            } catch (Throwable t) {                t.printStackTrace();            }        }    }}

all that is left is to load it into the context like so:

@Configuration@AutoConfigureAfter(CrshAutoConfiguration.class)public static class ShellConfiguration {    @Bean    InteractiveShellRunner runner(@Qualifier("shellBootstrap") PluginLifeCycle crshBootstrapBean){        return new InteractiveShellRunner(crshBootstrapBean);    }}