Hendy Irawan Hendy Irawan - 4 years ago 388
Bash Question

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

I'd like to run a Spring Boot application that embeds CRaSH shell, but instead of accessible via SSH/Telnet, I want CRaSH shell to start in the current console (i.e. direct/standalone) without any password, as soon as Spring finishes initializing all its beans.

When the user types

exit
or press Ctrl+D, the app should shutdown.

Also, SSH and Telnet support should be disabled.

PS. Bonus points if the app can read commands from stdin, e.g.

./crshapp < somefile.cmd

cat somefile.cmd | ./crshapp

Answer Source

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 console like so:

@Configuration
@AutoConfigureAfter(CrshAutoConfiguration.class)
public static class ShellConfiguration {

    @Bean
    InteractiveShellRunner runner(@Qualifier("shellBootstrap") PluginLifeCycle crshBootstrapBean){
        return new InteractiveShellRunner(crshBootstrapBean);
    }
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download