I have an abstract groovy class with some utility methods that I want to extend from other groovy command classes (for use in Spring Boot's remote shell). However, when I attempt to run the groovy command class, I get a CommandException
.
My groovy abstract class looks like the following.
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];
}
}
My groovy command class looks like the following.
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())
}
}
}
When I SSH into the shell and execute foo bar
I get the following exception.
org.crsh.shell.impl.command.spi.CommandException: Could not create command foo instance at org.crsh.lang.impl.groovy.GroovyCompiler$1.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]
If I remove the package commands
line from both the abstractcmd
and foo
classes, then the IDE (IntelliJ) and shell both complain about abstractcmd
not being found.
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$1.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
The only way that I am able to get a custom command to work properly if is I don't extend abstractcmd
(thereby implement the "util" methods in the command class) and remove the package
line.
I am using Spring Boot v1.5.1.RELEASE.
Any ideas on what I'm doing wrong?
Looks like CRaSH has issues with groovy classes extending other groovy classes, because if you don't extend one, it works just fine.
I'm sorry but I did not have time to fully investigate how and why (corrections/updates are more than welcome), and didn't find anything in the 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
abstract class with the following javadoc:
/**
* The base command for Groovy class based commands.
*/
public abstract class GroovyCommand extends BaseCommand implements GroovyObject
So, to cut a long story short, you could use the following workaround:
Abstract SpringAwareCommand
java class
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);
}
}
Updated foo
groovy class
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())
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.