Groovy 使用继承的命令无法在 Spring 远程引导中编译 shell

Groovy command using inheritance cannot compile in Spring Boot remote shell

我有一个抽象 groovy class,其中包含一些我想从其他 groovy 命令 classes 扩展的实用方法(用于 Spring 启动的远程 shell)。但是,当我尝试 运行 groovy 命令 class 时,我得到 CommandException.

我的 groovy 摘要 class 如下所示。

package commands 
import com.xyz.MyService
import org.crsh.command.InvocationContext
import org.springframework.beans.factory.BeanFactory

abstract class abstractcmd {
    private static final String SPRING_FACTORY = "spring.beanfactory"

    protected MyService getMyService(InvocationContext context) {
        return getBeanFactory(context).getBean(MyService.class);
    }

    private BeanFactory getBeanFactory(InvocationContext context) {
        return context.attributes[SPRING_FACTORY];
    }
}

我的 groovy 命令 class 如下所示。

package commands
import org.crsh.cli.Command
import org.crsh.cli.Usage
import org.crsh.command.InvocationContext

@Usage("do something commands")
class foo extends abstractcmd {
    @Command
    @Usage("bar")
    def String bar(InvocationContext context) {
        try {
            getMyService(context).bar()
            return "did bar"
        } catch (Exception e) {
            return String.format("could not do bar: %s", e.toString())
        }
    }
}

当我通过 SSH 进入 shell 并执行 foo bar 时,出现以下异常。

org.crsh.shell.impl.command.spi.CommandException: Could not create command foo instance
    at org.crsh.lang.impl.groovy.GroovyCompiler.getCommand(GroovyCompiler.java:192) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.lang.LanguageCommandResolver.resolveCommand(LanguageCommandResolver.java:101) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.shell.impl.command.CRaSH.getCommand(CRaSH.java:100) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.shell.impl.command.CRaSHSession.getCommand(CRaSHSession.java:96) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.lang.impl.script.PipeLineFactory.create(PipeLineFactory.java:89) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.lang.impl.script.ScriptRepl.eval(ScriptRepl.java:88) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.shell.impl.command.CRaSHSession.createProcess(CRaSHSession.java:163) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.shell.impl.async.AsyncProcess.execute(AsyncProcess.java:172) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.console.Console.iterate(Console.java:219) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.console.Console.on(Console.java:158) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.console.Console.on(Console.java:135) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.console.jline.JLineProcessor.run(JLineProcessor.java:204) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.ssh.term.CRaSHCommand.run(CRaSHCommand.java:99) ~[crash.connectors.ssh-1.3.2.jar:?]
    at java.lang.Thread.run(Thread.java:745) [?:1.8.0_111]

如果我从 abstractcmdfoo classes 中删除 package commands 行,那么 IDE (IntelliJ) 和 shell 都抱怨 abstractcmd 找不到。

2017-03-19 19:34:06 ERROR org.crsh.shell.impl.command.CRaSHProcess.execute:84 - Error while evaluating request 'foo' Could not compile command script foo
org.crsh.shell.impl.command.spi.CommandException: Could not compile command script foo
    at org.crsh.lang.impl.groovy.GroovyClassFactory.parse(GroovyClassFactory.java:65) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.lang.impl.groovy.GroovyCompiler.getCommand(GroovyCompiler.java:172) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.lang.LanguageCommandResolver.resolveCommand(LanguageCommandResolver.java:101) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.shell.impl.command.CRaSH.getCommand(CRaSH.java:100) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.shell.impl.command.CRaSHSession.getCommand(CRaSHSession.java:96) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.lang.impl.script.PipeLineFactory.create(PipeLineFactory.java:89) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.lang.impl.script.ScriptRepl.eval(ScriptRepl.java:88) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.shell.impl.command.CRaSHSession.createProcess(CRaSHSession.java:163) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.shell.impl.async.AsyncProcess.execute(AsyncProcess.java:172) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.console.Console.iterate(Console.java:219) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.console.Console.on(Console.java:158) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.console.Console.on(Console.java:135) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.console.jline.JLineProcessor.run(JLineProcessor.java:204) ~[crash.shell-1.3.2.jar:?]
    at org.crsh.ssh.term.CRaSHCommand.run(CRaSHCommand.java:99) ~[crash.connectors.ssh-1.3.2.jar:?]
    at java.lang.Thread.run(Thread.java:745) [?:1.8.0_111]
Caused by: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
foo: 9: unable to resolve class abstractcmd 
 @ line 9, column 1.
   @Usage("bar")
   ^

1 error

    at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:310) ~[groovy-2.4.7.jar:2.4.7]
    at org.codehaus.groovy.control.CompilationUnit.applyToSourceUnits(CompilationUnit.java:946) ~[groovy-2.4.7.jar:2.4.7]
    at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:593) ~[groovy-2.4.7.jar:2.4.7]
    at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:542) ~[groovy-2.4.7.jar:2.4.7]
    at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:298) ~[groovy-2.4.7.jar:2.4.7]
    at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:268) ~[groovy-2.4.7.jar:2.4.7]
    at org.crsh.lang.impl.groovy.GroovyClassFactory.parse(GroovyClassFactory.java:59) ~[crash.shell-1.3.2.jar:?]
    ... 14 more

如果我不扩展 abstractcmd(从而在命令 class 中实现 "util" 方法,我能够使自定义命令正常工作的唯一方法) 并删除 package 行。

我正在使用 Spring Boot v1.5.1.RELEASE。

对我做错了什么有什么想法吗?

看起来 CRaSH 在 groovy classes 扩展其他 groovy classes 方面有问题,因为如果你不扩展一个,它工作得很好。

很抱歉,我没有时间全面调查如何以及为什么(corrections/updates 非常受欢迎),并且在 docs but the good news is that you can extend an abstract java class to achieve the same thing. I stumbled upon such a sample for the cron command that extends a GroovyCommand 摘要中没有找到任何内容 class 与以下 javadoc:

/**
 * The base command for Groovy class based commands.
 */
public abstract class GroovyCommand extends BaseCommand implements GroovyObject

因此,长话短说,您可以使用以下解决方法:

摘要SpringAwareCommandjavaclass

package crash.commands;

import org.crsh.command.BaseCommand;
import org.springframework.beans.factory.BeanFactory;

public abstract class SpringAwareCommand extends BaseCommand {

    private static final String SPRING_BEAN_FACTORY = "spring.beanfactory";

    protected <T> T getBean(Class<T> beanClass) {
        return ((BeanFactory) this.context.getAttributes().get(SPRING_BEAN_FACTORY)).getBean(beanClass);
    }

}

已更新foogroovyclass

package crash.commands

import com.xyz.MyService
import org.crsh.cli.Command
import org.crsh.cli.Usage

@Usage("do something commands")
class foo extends SpringAwareCommand {
    @Command
    @Usage("bar")
    def String bar() {
        try {
            getBean(MyService.class).bar()
            return "did bar"
        } catch (Exception e) {
            return String.format("could not do bar: %s", e.toString())
        }
    }
}