vishal vishal - 2 months ago 40
Java Question

How to pass arguments and options to custom remote shell command in spring boot remote shell?

This is followup question on

how to build console command in spring boot web application using spring shell?

As per suggestions on above question I am trying spring boot remote shell.

I have created my custom command following this documentation,

http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-remote-shell.html#production-ready-extending-the-remote-shell

But in real world use cases, we mostly have arguments and options to command. There is no mention in doc about how to define them so that user should pass while running commands.

This is very well explained and easy to do in spring-shell.

http://docs.spring.io/spring-shell/docs/current/reference/htmlsingle/#simple-application

https://projects.spring.io/spring-shell/

But we cant use spring shell with spring boot.

what are the reliable production ready solutions to such use cases ?

Update:

Got the solution on CRaSH doc

http://www.crashub.org/1.3/reference.html#developping_commands

I can see myCommand in the list of commands after loggin into shell, but when I run command,

but I am getting exception

"Could not create command commandName instance".


I am trying to use spring beans in command so I did autowiring e.g.

package commands;

import org.crsh.cli.Argument;
import org.crsh.cli.Command;
import org.crsh.cli.Usage;
import org.crsh.cli.Option;
import org.crsh.cli.Required;
import org.crsh.command.BaseCommand;
import org.crsh.command.InvocationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;


@Usage("My test computation commands.")
public class myCommand extends BaseCommand {

protected final Logger log = LoggerFactory.getLogger(getClass());

/**
*
*/
public Service1 service1;

/**
*
*/
public Service2 service2;

/**
* @param service1
* @param service2
*/
@Autowired
public myCommand(Service1 service1, Service2 service2) {
super();
this.service1 = service1;
this.service2 = service2;
}


@Usage("Test command")
@Command
public Map<String, Double> command1(InvocationContext<Object> context, @Usage("id") @Required @Argument int id)
throws Exception {


Map<String, Double> result = new HashMap<>();

result.put("key1", service1.compute(id));
result.put("key2", service2.compute(id));

return result;

}

}

Answer

I don't think you can inject beans in your Remote shell command.

You may however inject a InvocationContext into your method and use it to retrieve a spring managed bean from your context:

@Usage('Example using spring.beanfactory')
@Command
def mycommand(InvocationContext context, ...) {
    BeanFactory beans = context.attributes['spring.beanfactory']
    YourBean bean = beans.getBean(YourBean.class);
    ...
}

A Complete example which works for me:

package commands

import org.crsh.cli.Command
import org.crsh.cli.Usage
import org.crsh.command.InvocationContext
import org.springframework.beans.factory.BeanFactory
import com.alexbt.goodies.MyBean

class SayMessage {
    String message;
    SayMessage(){
        this.message = "Hello";
    }

    @Usage("Default command")
    @Command
    def main(InvocationContext context, @Usage("A Parameter") @Option(names=["p","param"]) String param) {
        BeanFactory beanFactory = (BeanFactory) context.getAttributes().get("spring.beanfactory");
        MyBean bean = beanFactory.getBean(MyBean.class);
        return message + " " + bean.getValue() + " " + param;
    }

    @Usage("Hi subcommand")
    @Command
    def hi(InvocationContext context, @Usage("A Parameter") @Option(names=["p","param"]) String param) {
        BeanFactory beanFactory = (BeanFactory) context.getAttributes().get("spring.beanfactory");
        MyBean bean = beanFactory.getBean(MyBean.class);
        return "Hi " + bean.getValue() + " " + param;
    }
}

> saymsg -p Johnny
> Hello my friend Johnny

> saymsg hi -p Johnny
> Hi my friend Johnny