diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/SpringCli.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/SpringCli.java index ac61fe598e1..498b8c55b3f 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/SpringCli.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/SpringCli.java @@ -52,12 +52,15 @@ public class SpringCli { private String displayName = CLI_APP + " "; + private InitCommand init; + /** * Create a new {@link SpringCli} implementation with the default set of commands. */ public SpringCli() { try { - new InitCommand(this).run(); + this.init = new InitCommand(this); + this.init.run(); } catch (Exception e) { throw new IllegalStateException("Cannot init with those args", e); @@ -66,6 +69,10 @@ public class SpringCli { this.commands.add(new HintCommand()); } + public InitCommand getInitCommand() { + return this.init; + } + /** * Set the command available to the CLI. Primarily used to support testing. NOTE: The * 'help' command will be automatically provided in addition to this list. diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/InitCommand.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/InitCommand.java index 272f7f48520..97962758409 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/InitCommand.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/InitCommand.java @@ -16,11 +16,13 @@ package org.springframework.boot.cli.command; +import groovy.lang.Closure; import groovy.lang.GroovyClassLoader; import groovy.lang.Script; import java.io.File; import java.util.List; +import java.util.Map; import java.util.ServiceLoader; import joptsimple.OptionSet; @@ -87,6 +89,8 @@ public class InitCommand extends OptionParsingCommand { options, this, repositoryConfiguration); this.compiler = new GroovyCompiler(configuration); + this.compiler + .addCompilationCustomizers(new ScriptCompilationCustomizer()); loader = this.compiler.getLoader(); Thread.currentThread().setContextClassLoader(loader); @@ -102,7 +106,25 @@ public class InitCommand extends OptionParsingCommand { if (this.compiler != null && files.length > 0) { Class[] classes = this.compiler.compile(files); for (Class type : classes) { - if (Script.class.isAssignableFrom(type)) { + Command script = ScriptCommand.command(type); + if (script != null) { + this.cli.register(script); + } + else if (CommandFactory.class.isAssignableFrom(type)) { + for (Command command : ((CommandFactory) type.newInstance()) + .getCommands(this.cli)) { + this.cli.register(command); + } + } + else if (Commands.class.isAssignableFrom(type)) { + Map> commands = ((Commands) type.newInstance()) + .getCommands(); + for (String command : commands.keySet()) { + this.cli.register(new ScriptCommand(command, commands + .get(command))); + } + } + else if (Script.class.isAssignableFrom(type)) { ((Script) type.newInstance()).run(); } } @@ -121,7 +143,6 @@ public class InitCommand extends OptionParsingCommand { } } - } private static class InitGroovyCompilerConfigurationAdapter extends @@ -138,4 +159,8 @@ public class InitCommand extends OptionParsingCommand { } } + public static interface Commands { + Map> getCommands(); + } + } diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ScriptCommand.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ScriptCommand.java index c342a987697..0d838fa4fba 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ScriptCommand.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ScriptCommand.java @@ -17,30 +17,13 @@ package org.springframework.boot.cli.command; import groovy.lang.Closure; -import groovy.lang.GroovyObjectSupport; -import groovy.lang.MetaClass; -import groovy.lang.MetaMethod; -import groovy.lang.Script; +import groovy.lang.GroovyObject; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.URL; import java.util.Collection; import java.util.Collections; -import java.util.List; -import joptsimple.OptionParser; - -import org.codehaus.groovy.control.CompilationFailedException; import org.springframework.boot.cli.Command; import org.springframework.boot.cli.OptionHelp; -import org.springframework.boot.cli.compiler.GroovyCompiler; -import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration; -import org.springframework.boot.cli.compiler.GroovyCompilerScope; -import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory; -import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration; -import org.springframework.util.FileCopyUtils; /** * {@link Command} to run a Groovy script. @@ -49,27 +32,28 @@ import org.springframework.util.FileCopyUtils; */ public class ScriptCommand implements Command { - private static final String[] DEFAULT_PATHS = new String[] { "${SPRING_HOME}/ext", - "${SPRING_HOME}/bin" }; - - private String[] paths = DEFAULT_PATHS; - - private Class mainClass; - private Object main; - private String name; + private String defaultName; - public ScriptCommand(String script) { - this.name = script; + public ScriptCommand(String name, Object main) { + this.main = main; + this.defaultName = name; } @Override public String getName() { - if (getMain() instanceof Command) { - return ((Command) getMain()).getName(); + if (this.main instanceof Command) { + return ((Command) this.main).getName(); } - return this.name; + else if (this.main instanceof GroovyObject) { + GroovyObject object = (GroovyObject) this.main; + if (object.getMetaClass().hasProperty(object, "name") != null) { + return (String) object.getProperty("name"); + } + } + + return this.defaultName; } @Override @@ -79,210 +63,73 @@ public class ScriptCommand implements Command { @Override public String getDescription() { - if (getMain() instanceof Command) { - return ((Command) getMain()).getDescription(); + if (this.main instanceof Command) { + return ((Command) this.main).getDescription(); } - return this.name; + return this.defaultName; } @Override public String getHelp() { - if (getMain() instanceof OptionHandler) { - return ((OptionHandler) getMain()).getHelp(); + if (this.main instanceof OptionHandler) { + return ((OptionHandler) this.main).getHelp(); } - if (getMain() instanceof Command) { - return ((Command) getMain()).getHelp(); + if (this.main instanceof Command) { + return ((Command) this.main).getHelp(); } return null; } @Override public Collection getOptionsHelp() { - if (getMain() instanceof OptionHandler) { - return ((OptionHandler) getMain()).getOptionsHelp(); + if (this.main instanceof OptionHandler) { + return ((OptionHandler) this.main).getOptionsHelp(); } - if (getMain() instanceof Command) { - return ((Command) getMain()).getOptionsHelp(); + if (this.main instanceof Command) { + return ((Command) this.main).getOptionsHelp(); } return Collections.emptyList(); } @Override public void run(String... args) throws Exception { - run(getMain(), args); - } - - private void run(Object main, String[] args) throws Exception { - if (main instanceof Command) { - ((Command) main).run(args); + if (this.main instanceof Command) { + ((Command) this.main).run(args); } - else if (main instanceof OptionHandler) { - ((OptionHandler) getMain()).run(args); + else if (this.main instanceof OptionHandler) { + ((OptionHandler) this.main).run(args); } - else if (main instanceof Closure) { - ((Closure) main).call((Object[]) args); + else if (this.main instanceof Closure) { + ((Closure) this.main).call((Object[]) args); } - else if (main instanceof Runnable) { - ((Runnable) main).run(); - } - else if (main instanceof Script) { - Script script = (Script) this.main; - script.setProperty("args", args); - if (this.main instanceof GroovyObjectSupport) { - GroovyObjectSupport object = (GroovyObjectSupport) this.main; - if (object.getMetaClass().hasProperty(object, "parser") != null) { - OptionParser parser = (OptionParser) object.getProperty("parser"); - if (parser != null) { - script.setProperty("options", parser.parse(args)); - } - } - } - Object result = script.run(); - run(result, args); - } - } - - /** - * Paths to search for script files. - * - * @param paths the paths to set - */ - public void setPaths(String[] paths) { - this.paths = (paths == null ? null : paths.clone()); } @Override public String getUsageHelp() { - if (getMain() instanceof Command) { - return ((Command) getMain()).getDescription(); + if (this.main instanceof Command) { + return ((Command) this.main).getDescription(); } return "[options] "; } - protected Object getMain() { - if (this.main == null) { - try { - this.main = getMainClass().newInstance(); - } - catch (Exception ex) { - throw new IllegalStateException("Cannot create main class: " + this.name, - ex); - } - if (this.main instanceof OptionHandler) { - ((OptionHandler) this.main).options(); - } - else if (this.main instanceof GroovyObjectSupport) { - GroovyObjectSupport object = (GroovyObjectSupport) this.main; - MetaClass metaClass = object.getMetaClass(); - MetaMethod options = metaClass.getMetaMethod("options", null); - if (options != null) { - options.doMethodInvoke(this.main, null); - } - } - } - return this.main; - } + public static ScriptCommand command(Class type) { - private void compile() { - GroovyCompiler compiler = new GroovyCompiler(new ScriptConfiguration()); - compiler.addCompilationCustomizers(new ScriptCompilationCustomizer()); - File source = locateSource(this.name); - Class[] classes; + Object main = null; try { - classes = compiler.compile(source); + main = type.newInstance(); } - catch (CompilationFailedException ex) { - throw new IllegalStateException("Could not compile script", ex); + catch (Exception ex) { + // Inner classes and closures will not be instantiatable + return null; } - catch (IOException ex) { - throw new IllegalStateException("Could not compile script", ex); + if (main instanceof Command) { + return new ScriptCommand(type.getSimpleName(), main); } - this.mainClass = classes[0]; - } - - private Class getMainClass() { - if (this.mainClass == null) { - compile(); - } - return this.mainClass; - } - - private File locateSource(String name) { - String resource = name; - if (!name.endsWith(".groovy")) { - resource = "commands/" + name + ".groovy"; - } - - URL url = getClass().getClassLoader().getResource(resource); - if (url != null) { - return locateSourceFromUrl(name, url); - } - - String home = System.getProperty("SPRING_HOME", System.getenv("SPRING_HOME")); - if (home == null) { - home = "."; - } - - for (String path : this.paths) { - String subbed = path.replace("${SPRING_HOME}", home); - File file = new File(subbed, resource); - if (file.exists()) { - return file; - } - } - - throw new IllegalStateException("No script found for : " + name); - } - - private File locateSourceFromUrl(String name, URL url) { - if (url.toString().startsWith("file:")) { - return new File(url.toString().substring("file:".length())); - } - - // probably in JAR file - try { - File file = File.createTempFile(name, ".groovy"); - file.deleteOnExit(); - FileCopyUtils.copy(url.openStream(), new FileOutputStream(file)); - return file; - } - catch (IOException ex) { - throw new IllegalStateException("Could not create temp file for source: " - + name); - } - } - - private static class ScriptConfiguration implements GroovyCompilerConfiguration { - - @Override - public GroovyCompilerScope getScope() { - return GroovyCompilerScope.EXTENSION; - } - - @Override - public boolean isGuessImports() { - return true; - } - - @Override - public boolean isAutoconfigure() { - return true; - } - - @Override - public boolean isGuessDependencies() { - return true; - } - - @Override - public String[] getClasspath() { - return DEFAULT_CLASSPATH; - } - - @Override - public List getRepositoryConfiguration() { - return RepositoryConfigurationFactory.createDefaultRepositoryConfiguration(); + else if (main instanceof OptionHandler) { + ((OptionHandler) main).options(); + return new ScriptCommand(type.getSimpleName(), main); } + return null; } diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ScriptCompilationCustomizer.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ScriptCompilationCustomizer.java index 0761679d007..610c10df946 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ScriptCompilationCustomizer.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ScriptCompilationCustomizer.java @@ -16,8 +16,7 @@ package org.springframework.boot.cli.command; -import groovy.lang.Mixin; - +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; @@ -25,15 +24,17 @@ import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.OptionSpecBuilder; -import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassCodeVisitorSupport; import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.PropertyNode; import org.codehaus.groovy.ast.expr.ArgumentListExpression; -import org.codehaus.groovy.ast.expr.ClassExpression; import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.MapExpression; import org.codehaus.groovy.ast.expr.MethodCallExpression; import org.codehaus.groovy.ast.stmt.BlockStatement; import org.codehaus.groovy.ast.stmt.ExpressionStatement; @@ -46,6 +47,7 @@ import org.codehaus.groovy.control.customizers.CompilationCustomizer; import org.codehaus.groovy.control.customizers.ImportCustomizer; import org.springframework.asm.Opcodes; import org.springframework.boot.cli.Command; +import org.springframework.boot.cli.command.InitCommand.Commands; /** * Customizer for the compilation of CLI commands. @@ -61,11 +63,80 @@ public class ScriptCompilationCustomizer extends CompilationCustomizer { @Override public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { - addOptionHandlerMixin(classNode); + findCommands(source, classNode); overrideOptionsMethod(source, classNode); addImports(source, context, classNode); } + private void findCommands(SourceUnit source, ClassNode classNode) { + CommandVisitor visitor = new CommandVisitor(source); + classNode.visitContents(visitor); + visitor.addFactory(classNode); + } + + private static class CommandVisitor extends ClassCodeVisitorSupport { + + private SourceUnit source; + private MapExpression map = new MapExpression(); + private List statements = new ArrayList(); + private ExpressionStatement statement; + + public CommandVisitor(SourceUnit source) { + this.source = source; + } + + private boolean hasCommands() { + return !this.map.getMapEntryExpressions().isEmpty(); + } + + private void addFactory(ClassNode classNode) { + if (!hasCommands()) { + return; + } + classNode.addInterface(ClassHelper.make(Commands.class)); + classNode.addProperty(new PropertyNode("commands", Modifier.PUBLIC + | Modifier.FINAL, ClassHelper.MAP_TYPE.getPlainNodeReference(), + classNode, this.map, null, null)); + } + + @Override + protected SourceUnit getSourceUnit() { + return this.source; + } + + @Override + public void visitBlockStatement(BlockStatement block) { + this.statements.clear(); + super.visitBlockStatement(block); + block.getStatements().removeAll(this.statements); + } + + @Override + public void visitExpressionStatement(ExpressionStatement statement) { + this.statement = statement; + super.visitExpressionStatement(statement); + } + + @Override + public void visitMethodCallExpression(MethodCallExpression call) { + Expression methodCall = call.getMethod(); + if (methodCall instanceof ConstantExpression) { + ConstantExpression method = (ConstantExpression) methodCall; + if ("command".equals(method.getValue())) { + ArgumentListExpression arguments = (ArgumentListExpression) call + .getArguments(); + this.statements.add(this.statement); + ConstantExpression name = (ConstantExpression) arguments + .getExpression(0); + ClosureExpression closure = (ClosureExpression) arguments + .getExpression(1); + this.map.addMapEntryExpression(name, closure); + } + } + } + + } + /** * Add imports to the class node to make writing simple commands easier. No need to * import {@link OptionParser}, {@link OptionSet}, {@link Command} or @@ -104,6 +175,18 @@ public class ScriptCompilationCustomizer extends CompilationCustomizer { */ private void overrideOptionsMethod(SourceUnit source, ClassNode classNode) { + ClosureExpression closure = options(source, classNode); + if (closure != null) { + classNode.addMethod(new MethodNode("options", Opcodes.ACC_PROTECTED, + ClassHelper.VOID_TYPE, new Parameter[0], new ClassNode[0], closure + .getCode())); + classNode.setSuperClass(ClassHelper.make(OptionHandler.class)); + } + + } + + private ClosureExpression options(SourceUnit source, ClassNode classNode) { + BlockStatement block = source.getAST().getStatementBlock(); List statements = block.getStatements(); @@ -114,43 +197,22 @@ public class ScriptCompilationCustomizer extends CompilationCustomizer { if (expression instanceof MethodCallExpression) { MethodCallExpression method = (MethodCallExpression) expression; if (method.getMethod().getText().equals("options")) { + statements.remove(statement); expression = method.getArguments(); if (expression instanceof ArgumentListExpression) { ArgumentListExpression arguments = (ArgumentListExpression) expression; expression = arguments.getExpression(0); - if (expression instanceof ClosureExpression) { - ClosureExpression closure = (ClosureExpression) expression; - classNode.addMethod(new MethodNode("options", - Opcodes.ACC_PROTECTED, ClassHelper.VOID_TYPE, - new Parameter[0], new ClassNode[0], closure - .getCode())); - statements.remove(statement); + return (ClosureExpression) expression; } - } } } } } - } + return null; - /** - * Add {@link OptionHandler} as a mixin to the class node if it doesn't already - * declare it as a super class. - * - * @param classNode the class node to manipulate - */ - private void addOptionHandlerMixin(ClassNode classNode) { - // If we are not an OptionHandler then add that class as a mixin - if (!classNode.isDerivedFrom(ClassHelper.make(OptionHandler.class)) - && !classNode.isDerivedFrom(ClassHelper.make("OptionHandler"))) { - AnnotationNode mixin = new AnnotationNode(ClassHelper.make(Mixin.class)); - mixin.addMember("value", - new ClassExpression(ClassHelper.make(OptionHandler.class))); - classNode.addAnnotation(mixin); - } } } diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ShellCommand.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ShellCommand.java index 31d67e38ae8..a7f4c50a239 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ShellCommand.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ShellCommand.java @@ -99,7 +99,7 @@ public class ShellCommand extends AbstractCommand { PromptCommand prompt = new PromptCommand(this); cli.register(prompt); - cli.register(new InitCommand(cli)); + cli.register(cli.getInitCommand()); } private ConsoleReader createConsoleReader() throws IOException { diff --git a/spring-boot-cli/src/test/java/cli/command/ScriptCompilationCustomizerTests.java b/spring-boot-cli/src/test/java/cli/command/ScriptCompilationCustomizerTests.java new file mode 100644 index 00000000000..e08fe7b0b8b --- /dev/null +++ b/spring-boot-cli/src/test/java/cli/command/ScriptCompilationCustomizerTests.java @@ -0,0 +1,131 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cli.command; + +import groovy.lang.Closure; + +import java.io.File; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.springframework.boot.OutputCapture; +import org.springframework.boot.cli.Command; +import org.springframework.boot.cli.command.InitCommand.Commands; +import org.springframework.boot.cli.command.OptionHandler; +import org.springframework.boot.cli.command.ScriptCompilationCustomizer; +import org.springframework.boot.cli.compiler.GroovyCompiler; +import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration; +import org.springframework.boot.cli.compiler.GroovyCompilerScope; +import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author Dave Syer + */ +public class ScriptCompilationCustomizerTests { + + private TestGroovyCompilerConfiguration configuration = new TestGroovyCompilerConfiguration(); + private GroovyCompiler compiler = new GroovyCompiler(this.configuration); + + @Rule + public OutputCapture output = new OutputCapture(); + + @Before + public void init() { + this.compiler.addCompilationCustomizers(new ScriptCompilationCustomizer()); + } + + @Test + public void simpleCompile() throws Exception { + Class[] types = this.compiler.compile(new File( + "src/test/resources/scripts/command.groovy")); + Class main = types[0]; + assertEquals("org.test.command.TestCommand", main.getName()); + assertTrue(Command.class.isAssignableFrom(main)); + } + + @Test + public void addsOptionHandler() throws Exception { + Class[] types = this.compiler.compile(new File( + "src/test/resources/scripts/handler.groovy")); + Class main = types[0]; + assertTrue(OptionHandler.class.isAssignableFrom(main)); + } + + @Test + public void addsCommands() throws Exception { + Class[] types = this.compiler.compile(new File( + "src/test/resources/scripts/options.groovy")); + Class main = types[0]; + assertTrue(Commands.class.isAssignableFrom(main)); + } + + @Test + public void commandsExecutable() throws Exception { + Class[] types = this.compiler.compile(new File( + "src/test/resources/scripts/options.groovy")); + Class main = types[0]; + Map> commands = ((Commands) main.newInstance()).getCommands(); + assertEquals(1, commands.size()); + assertEquals("foo", commands.keySet().iterator().next()); + Closure closure = commands.values().iterator().next(); + closure.call(); // what about args? + assertTrue(this.output.toString().contains("Hello Command")); + } + + private static class TestGroovyCompilerConfiguration implements + GroovyCompilerConfiguration { + + @Override + public GroovyCompilerScope getScope() { + return GroovyCompilerScope.EXTENSION; + } + + @Override + public boolean isGuessImports() { + return true; + } + + @Override + public boolean isGuessDependencies() { + return false; + } + + @Override + public boolean isAutoconfigure() { + return true; + } + + @Override + public String[] getClasspath() { + return new String[0]; + } + + @Override + public List getRepositoryConfiguration() { + return Collections.emptyList(); + } + + } + +} diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/InitCommandTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/InitCommandTests.java index 5587618e009..67105dde5f9 100644 --- a/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/InitCommandTests.java +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/InitCommandTests.java @@ -69,6 +69,12 @@ public class InitCommandTests { assertTrue(this.output.toString().contains("Hello Grab")); } + @Test + public void initCommand() throws Exception { + this.command.run("src/test/resources/command.groovy"); + verify(this.cli, times(this.defaultCount + 1)).register(any(Command.class)); + } + @Test(expected = IllegalArgumentException.class) public void initNonExistentScript() throws Exception { this.command.run("nonexistent.groovy"); @@ -78,7 +84,7 @@ public class InitCommandTests { @Test public void initDefault() throws Exception { this.command.run(); - assertTrue(this.output.toString().contains("Hello World")); + assertTrue(this.output.toString().contains("Hello Init")); } } diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/ScriptCommandTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/ScriptCommandTests.java new file mode 100644 index 00000000000..78132d2e288 --- /dev/null +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/ScriptCommandTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.cli.command; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.springframework.boot.OutputCapture; +import org.springframework.boot.cli.SpringCli; + +import static org.junit.Assert.assertTrue; + +/** + * @author Dave Syer + */ +public class ScriptCommandTests { + + @Rule + public OutputCapture output = new OutputCapture(); + + public static boolean executed = false; + + private SpringCli cli; + private InitCommand init; + + private ClassLoader classLoader; + + @Before + public void init() { + this.classLoader = Thread.currentThread().getContextClassLoader(); + this.cli = new SpringCli(); + this.init = this.cli.getInitCommand(); + executed = false; + } + + @After + public void close() { + Thread.currentThread().setContextClassLoader(this.classLoader); + } + + @Test + public void command() throws Exception { + this.init.run("src/test/resources/commands/command.groovy"); + this.cli.find("foo").run("Foo"); + assertTrue(this.output.toString().contains("Hello Foo")); + } + + @Test + public void handler() throws Exception { + this.init.run("src/test/resources/commands/handler.groovy"); + this.cli.find("foo").run("Foo", "--foo=bar"); + assertTrue(executed); + } + + @Test + @Ignore + public void options() throws Exception { + this.init.run("src/test/resources/commands/options.groovy"); + this.cli.find("foo").run("Foo", "--foo=bar"); + assertTrue(executed); + } + +} diff --git a/spring-boot-cli/src/test/resources/command.groovy b/spring-boot-cli/src/test/resources/command.groovy new file mode 100644 index 00000000000..08f5ed71487 --- /dev/null +++ b/spring-boot-cli/src/test/resources/command.groovy @@ -0,0 +1,19 @@ +class MyCommand implements Command { + + String name = "foo" + + String description = "My script command" + + String help = "No options" + + String usageHelp = "Not very useful" + + Collection optionsHelp = ["No options"] + + boolean optionCommand = false + + void run(String... args) { + println "Hello ${args[0]}" + } + +} \ No newline at end of file diff --git a/spring-boot-cli/src/test/resources/commands/command.groovy b/spring-boot-cli/src/test/resources/commands/command.groovy index c79bdac7c36..f756ad4c9a8 100644 --- a/spring-boot-cli/src/test/resources/commands/command.groovy +++ b/spring-boot-cli/src/test/resources/commands/command.groovy @@ -16,6 +16,8 @@ package org.test.command +import java.util.Collection; + class TestCommand implements Command { String name = "foo" @@ -25,9 +27,12 @@ class TestCommand implements Command { String help = "No options" String usageHelp = "Not very useful" + + Collection optionsHelp = ["No options"] + + boolean optionCommand = false void run(String... args) { - org.springframework.boot.cli.command.ScriptCommandTests.executed = true println "Hello ${args[0]}" } diff --git a/spring-boot-cli/src/test/resources/commands/handler.groovy b/spring-boot-cli/src/test/resources/commands/handler.groovy index 458ce4c619a..9b320e01fda 100644 --- a/spring-boot-cli/src/test/resources/commands/handler.groovy +++ b/spring-boot-cli/src/test/resources/commands/handler.groovy @@ -16,13 +16,13 @@ package org.test.command -import joptsimple.OptionSet - @Grab("org.eclipse.jgit:org.eclipse.jgit:2.3.1.201302201838-r") import org.eclipse.jgit.api.Git class TestCommand extends OptionHandler { + + String name = "foo" void options() { option "foo", "Foo set" @@ -30,7 +30,7 @@ class TestCommand extends OptionHandler { void run(OptionSet options) { // Demonstrate use of Grape.grab to load dependencies before running - println "Clean : " + Git.open(".." as File).status().call().isClean() + println "Clean: " + Git.open(".." as File).status().call().isClean() org.springframework.boot.cli.command.ScriptCommandTests.executed = true println "Hello ${options.nonOptionArguments()}: ${options.has('foo')}" } diff --git a/spring-boot-cli/src/test/resources/commands/closure.groovy b/spring-boot-cli/src/test/resources/commands/options.groovy similarity index 85% rename from spring-boot-cli/src/test/resources/commands/closure.groovy rename to spring-boot-cli/src/test/resources/commands/options.groovy index 98e4f3fb88a..c4475d623db 100644 --- a/spring-boot-cli/src/test/resources/commands/closure.groovy +++ b/spring-boot-cli/src/test/resources/commands/options.groovy @@ -14,9 +14,8 @@ * limitations under the License. */ -def run = { msg -> - org.springframework.boot.cli.command.ScriptCommandTests.executed = true - println "Hello ${msg}" -} +command("foo") { args -> -run + org.springframework.boot.cli.command.ScriptCommandTests.executed = true + println "Hello ${options.nonOptionArguments()}: ${options.has('foo')} ${options.valueOf('bar')}" +} diff --git a/spring-boot-cli/src/test/resources/commands/test.groovy b/spring-boot-cli/src/test/resources/commands/test.groovy deleted file mode 100644 index d3d176d4db7..00000000000 --- a/spring-boot-cli/src/test/resources/commands/test.groovy +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2012-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -options { - option "foo", "Foo set" - option "bar", "Bar has an argument of type int" withOptionalArg() ofType Integer -} - -org.springframework.boot.cli.command.ScriptCommandTests.executed = true -println "Hello ${options.nonOptionArguments()}: ${options.has('foo')} ${options.valueOf('bar')}" diff --git a/spring-boot-cli/src/test/resources/init.groovy b/spring-boot-cli/src/test/resources/init.groovy index 31665bffc0d..ede814588b6 100644 --- a/spring-boot-cli/src/test/resources/init.groovy +++ b/spring-boot-cli/src/test/resources/init.groovy @@ -1 +1 @@ -println "Hello World" \ No newline at end of file +println "Hello Init" \ No newline at end of file diff --git a/spring-boot-cli/src/test/resources/commands/runnable.groovy b/spring-boot-cli/src/test/resources/scripts/command.groovy similarity index 62% rename from spring-boot-cli/src/test/resources/commands/runnable.groovy rename to spring-boot-cli/src/test/resources/scripts/command.groovy index a17bb021df7..f756ad4c9a8 100644 --- a/spring-boot-cli/src/test/resources/commands/runnable.groovy +++ b/spring-boot-cli/src/test/resources/scripts/command.groovy @@ -14,14 +14,26 @@ * limitations under the License. */ -class TestCommand implements Runnable { - def msg - TestCommand(String msg) { - this.msg = msg - } - void run() { - org.springframework.boot.cli.command.ScriptCommandTests.executed = true - println "Hello ${msg}" +package org.test.command + +import java.util.Collection; + +class TestCommand implements Command { + + String name = "foo" + + String description = "My script command" + + String help = "No options" + + String usageHelp = "Not very useful" + + Collection optionsHelp = ["No options"] + + boolean optionCommand = false + + void run(String... args) { + println "Hello ${args[0]}" } + } -new TestCommand(args[0]) diff --git a/spring-boot-cli/src/test/resources/commands/mixin.groovy b/spring-boot-cli/src/test/resources/scripts/handler.groovy similarity index 56% rename from spring-boot-cli/src/test/resources/commands/mixin.groovy rename to spring-boot-cli/src/test/resources/scripts/handler.groovy index 6b394ae134b..aa5db82effd 100644 --- a/spring-boot-cli/src/test/resources/commands/mixin.groovy +++ b/spring-boot-cli/src/test/resources/scripts/handler.groovy @@ -14,9 +14,24 @@ * limitations under the License. */ -void options() { - option "foo", "Foo set" -} +package org.test.command -org.springframework.boot.cli.command.ScriptCommandTests.executed = true -println "Hello ${options.nonOptionArguments()}: ${options.has('foo')}" +@Grab("org.eclipse.jgit:org.eclipse.jgit:2.3.1.201302201838-r") +import org.eclipse.jgit.api.Git + + +class TestCommand extends OptionHandler { + + String name = "foo" + + void options() { + option "foo", "Foo set" + } + + void run(OptionSet options) { + // Demonstrate use of Grape.grab to load dependencies before running + println "Clean: " + Git.open(".." as File).status().call().isClean() + println "Hello ${options.nonOptionArguments()}: ${options.has('foo')}" + } + +} diff --git a/spring-boot-cli/src/test/resources/commands/script.groovy b/spring-boot-cli/src/test/resources/scripts/options.groovy similarity index 92% rename from spring-boot-cli/src/test/resources/commands/script.groovy rename to spring-boot-cli/src/test/resources/scripts/options.groovy index 19097575da5..795100d6445 100644 --- a/spring-boot-cli/src/test/resources/commands/script.groovy +++ b/spring-boot-cli/src/test/resources/scripts/options.groovy @@ -14,4 +14,8 @@ * limitations under the License. */ -println "Hello ${args[0]}" +command("foo") { args -> + + println "Hello Command" + +}