spring-shell icon indicating copy to clipboard operation
spring-shell copied to clipboard

Add the possibility to start the interactive mode after the end of the non-interactive mode

Open Xinerfapre opened this issue 3 years ago • 5 comments
trafficstars

Is it possible to add the possibility to start the interactive mode after the end of the non-interactive mode?

Xinerfapre avatar Oct 08 '22 19:10 Xinerfapre

Currently no, what would be a use case for this?

jvalkeal avatar Oct 10 '22 09:10 jvalkeal

If possible, I would like to start my application with non-interactive commands, for example to configure with input data the thread that will be started and then use interactive commands to interact with the thread.

Xinerfapre avatar Oct 10 '22 16:10 Xinerfapre

It may be difficult to implement but I see how this could by useful. Traditionally these types of "preparations" are done withing interactive session with additional commands, like "connect/disconnect to env/server" and then commands can dynamically come and go.

Going first via non-interactive would remove one step user needs to do.

jvalkeal avatar Oct 12 '22 07:10 jvalkeal

Can't something like this in the run method of the DefaultShellApplicationRunner class do the job?

@Override
public void run(ApplicationArguments args) throws Exception {
    log.debug("Checking shell runners {}", shellRunners);
    // Optional<ShellRunner> optional = shellRunners.stream().filter(sh ->
    // sh.canRun(args)).findFirst();
    // ShellRunner shellRunner = optional.orElse(null);
    shellRunners.stream().filter(sh -> sh.canRun(args)).forEachOrdered(shellRunner -> {
        logger.debug("Using shell runner {}", shellRunner);
        if (shellRunner != null) {
            try {
                shellRunner.run(args);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

Or something like this for the exception.

@Override
public void run(ApplicationArguments args) throws Exception {
	log.debug("Checking shell runners {}", shellRunners);
	//Optional<ShellRunner> optional = shellRunners.stream()
	//		.filter(sh -> sh.canRun(args))
	//		.findFirst();
	//ShellRunner shellRunner = optional.orElse(null);
	for (ShellRunner shellRunner : shellRunners.stream().filter(sh -> sh.canRun(args)).toArray(ShellRunner[]::new)) {
		log.debug("Using shell runner {}", shellRunner);
		if (shellRunner != null) {
			shellRunner.run(args);
		}
	}
}

Xinerfapre avatar Apr 05 '23 17:04 Xinerfapre

If anyone wants :

package myproject.myshell;

import java.util.Collections;
import java.util.List;
import java.util.Optional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.Order;
import org.springframework.shell.ShellApplicationRunner;
import org.springframework.shell.ShellRunner;
import org.springframework.stereotype.Component;

@Component
@Order(MyShellApplicationRunner.PRECEDENCE)
public class MyShellApplicationRunner implements ShellApplicationRunner {
    @Value("${myshell.interactive-after-noninteractive:false}")
    boolean interAfterNInter;
    /**
     * The precedence at which this runner is executed with respect to other
     * ApplicationRunner beans
     */
    public static final int PRECEDENCE = 0;
    private static final Logger logger = LoggerFactory
            .getLogger(MyShellApplicationRunner.class);
    private final List<ShellRunner> shellRunners;

    public MyShellApplicationRunner(List<ShellRunner> shellRunners) {
        Collections.sort(shellRunners, new AnnotationAwareOrderComparator());
        this.shellRunners = shellRunners;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        logger.debug("Checking shell runners {}", shellRunners);
        if (interAfterNInter)
            for (ShellRunner shellRunner : shellRunners.stream()
                    .filter(sh -> sh.canRun(args))
                    .toArray(ShellRunner[]::new)) {
                runShellRunner(shellRunner, args);
            }
        else {
            Optional<ShellRunner> optional = shellRunners.stream()
                    .filter(sh -> sh.canRun(args))
                    .findFirst();
            ShellRunner shellRunner = optional.orElse(null);
            runShellRunner(shellRunner, args);
        }
    }

    private void runShellRunner(ShellRunner shellRunner,
            ApplicationArguments args) throws Exception {
        logger.debug("Using shell runner {}", shellRunner);
        if (shellRunner != null) {
            shellRunner.run(args);
        }
    }
}

And you can enable or disable it in your configuration file (".properties", ".yml") with :

myshell.interactive-after-noninteractive = true # Default value is false
myshell:
    interactive-after-noninteractive: true # Default value is false

Xinerfapre avatar Jun 12 '23 19:06 Xinerfapre