|
|
|
@ -17,30 +17,13 @@ |
|
|
|
package org.springframework.boot.cli.command; |
|
|
|
package org.springframework.boot.cli.command; |
|
|
|
|
|
|
|
|
|
|
|
import groovy.lang.Closure; |
|
|
|
import groovy.lang.Closure; |
|
|
|
import groovy.lang.GroovyObjectSupport; |
|
|
|
import groovy.lang.GroovyObject; |
|
|
|
import groovy.lang.MetaClass; |
|
|
|
|
|
|
|
import groovy.lang.MetaMethod; |
|
|
|
|
|
|
|
import groovy.lang.Script; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import java.io.File; |
|
|
|
|
|
|
|
import java.io.FileOutputStream; |
|
|
|
|
|
|
|
import java.io.IOException; |
|
|
|
|
|
|
|
import java.net.URL; |
|
|
|
|
|
|
|
import java.util.Collection; |
|
|
|
import java.util.Collection; |
|
|
|
import java.util.Collections; |
|
|
|
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.Command; |
|
|
|
import org.springframework.boot.cli.OptionHelp; |
|
|
|
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. |
|
|
|
* {@link Command} to run a Groovy script. |
|
|
|
@ -49,27 +32,28 @@ import org.springframework.util.FileCopyUtils; |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public class ScriptCommand implements Command { |
|
|
|
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 Object main; |
|
|
|
|
|
|
|
|
|
|
|
private String name; |
|
|
|
private String defaultName; |
|
|
|
|
|
|
|
|
|
|
|
public ScriptCommand(String script) { |
|
|
|
public ScriptCommand(String name, Object main) { |
|
|
|
this.name = script; |
|
|
|
this.main = main; |
|
|
|
|
|
|
|
this.defaultName = name; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public String getName() { |
|
|
|
public String getName() { |
|
|
|
if (getMain() instanceof Command) { |
|
|
|
if (this.main instanceof Command) { |
|
|
|
return ((Command) getMain()).getName(); |
|
|
|
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 |
|
|
|
@Override |
|
|
|
@ -79,210 +63,73 @@ public class ScriptCommand implements Command { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public String getDescription() { |
|
|
|
public String getDescription() { |
|
|
|
if (getMain() instanceof Command) { |
|
|
|
if (this.main instanceof Command) { |
|
|
|
return ((Command) getMain()).getDescription(); |
|
|
|
return ((Command) this.main).getDescription(); |
|
|
|
} |
|
|
|
} |
|
|
|
return this.name; |
|
|
|
return this.defaultName; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public String getHelp() { |
|
|
|
public String getHelp() { |
|
|
|
if (getMain() instanceof OptionHandler) { |
|
|
|
if (this.main instanceof OptionHandler) { |
|
|
|
return ((OptionHandler) getMain()).getHelp(); |
|
|
|
return ((OptionHandler) this.main).getHelp(); |
|
|
|
} |
|
|
|
} |
|
|
|
if (getMain() instanceof Command) { |
|
|
|
if (this.main instanceof Command) { |
|
|
|
return ((Command) getMain()).getHelp(); |
|
|
|
return ((Command) this.main).getHelp(); |
|
|
|
} |
|
|
|
} |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Collection<OptionHelp> getOptionsHelp() { |
|
|
|
public Collection<OptionHelp> getOptionsHelp() { |
|
|
|
if (getMain() instanceof OptionHandler) { |
|
|
|
if (this.main instanceof OptionHandler) { |
|
|
|
return ((OptionHandler) getMain()).getOptionsHelp(); |
|
|
|
return ((OptionHandler) this.main).getOptionsHelp(); |
|
|
|
} |
|
|
|
} |
|
|
|
if (getMain() instanceof Command) { |
|
|
|
if (this.main instanceof Command) { |
|
|
|
return ((Command) getMain()).getOptionsHelp(); |
|
|
|
return ((Command) this.main).getOptionsHelp(); |
|
|
|
} |
|
|
|
} |
|
|
|
return Collections.emptyList(); |
|
|
|
return Collections.emptyList(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run(String... args) throws Exception { |
|
|
|
public void run(String... args) throws Exception { |
|
|
|
run(getMain(), args); |
|
|
|
if (this.main instanceof Command) { |
|
|
|
} |
|
|
|
((Command) this.main).run(args); |
|
|
|
|
|
|
|
|
|
|
|
private void run(Object main, String[] args) throws Exception { |
|
|
|
|
|
|
|
if (main instanceof Command) { |
|
|
|
|
|
|
|
((Command) main).run(args); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else if (main instanceof OptionHandler) { |
|
|
|
|
|
|
|
((OptionHandler) getMain()).run(args); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
else if (main instanceof Closure) { |
|
|
|
else if (this.main instanceof OptionHandler) { |
|
|
|
((Closure<?>) main).call((Object[]) args); |
|
|
|
((OptionHandler) this.main).run(args); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (main instanceof Runnable) { |
|
|
|
else if (this.main instanceof Closure) { |
|
|
|
((Runnable) main).run(); |
|
|
|
((Closure<?>) this.main).call((Object[]) args); |
|
|
|
} |
|
|
|
} |
|
|
|
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 |
|
|
|
@Override |
|
|
|
public String getUsageHelp() { |
|
|
|
public String getUsageHelp() { |
|
|
|
if (getMain() instanceof Command) { |
|
|
|
if (this.main instanceof Command) { |
|
|
|
return ((Command) getMain()).getDescription(); |
|
|
|
return ((Command) this.main).getDescription(); |
|
|
|
} |
|
|
|
} |
|
|
|
return "[options] <args>"; |
|
|
|
return "[options] <args>"; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected Object getMain() { |
|
|
|
public static ScriptCommand command(Class<?> type) { |
|
|
|
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; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void compile() { |
|
|
|
Object main = null; |
|
|
|
GroovyCompiler compiler = new GroovyCompiler(new ScriptConfiguration()); |
|
|
|
|
|
|
|
compiler.addCompilationCustomizers(new ScriptCompilationCustomizer()); |
|
|
|
|
|
|
|
File source = locateSource(this.name); |
|
|
|
|
|
|
|
Class<?>[] classes; |
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
classes = compiler.compile(source); |
|
|
|
main = type.newInstance(); |
|
|
|
} |
|
|
|
|
|
|
|
catch (CompilationFailedException ex) { |
|
|
|
|
|
|
|
throw new IllegalStateException("Could not compile script", ex); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
catch (IOException ex) { |
|
|
|
catch (Exception ex) { |
|
|
|
throw new IllegalStateException("Could not compile script", ex); |
|
|
|
// Inner classes and closures will not be instantiatable
|
|
|
|
|
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
this.mainClass = classes[0]; |
|
|
|
if (main instanceof Command) { |
|
|
|
} |
|
|
|
return new ScriptCommand(type.getSimpleName(), main); |
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
else if (main instanceof OptionHandler) { |
|
|
|
@Override |
|
|
|
((OptionHandler) main).options(); |
|
|
|
public List<RepositoryConfiguration> getRepositoryConfiguration() { |
|
|
|
return new ScriptCommand(type.getSimpleName(), main); |
|
|
|
return RepositoryConfigurationFactory.createDefaultRepositoryConfiguration(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|