mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
[bs-135] Plugin model for spring commands
* Added CommandFactory and a ServiceLoader model for providing implementations * Added ScriptCommand (wrapping groovy script). Service providers are recommended to implement OptionHandler in their script, but a regulare Script or a Runnable will also work. [#50427095]
This commit is contained in:
parent
51e6c8c8b6
commit
d950d15f9b
@ -1,163 +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.
|
|
||||||
*/
|
|
||||||
package org.springframework.bootstrap.cli;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
import joptsimple.OptionParser;
|
|
||||||
import joptsimple.OptionSet;
|
|
||||||
import joptsimple.OptionSpec;
|
|
||||||
|
|
||||||
import org.apache.ivy.util.FileUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link Command} to 'clean' up grapes, removing cached dependencies and forcing a
|
|
||||||
* download on the next attempt to resolve.
|
|
||||||
*
|
|
||||||
* @author Dave Syer
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class CleanCommand extends OptionParsingCommand {
|
|
||||||
|
|
||||||
private static enum Layout {
|
|
||||||
IVY, MAVEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
private OptionSpec<Void> allOption;
|
|
||||||
|
|
||||||
private OptionSpec<Void> ivyOption;
|
|
||||||
|
|
||||||
private OptionSpec<Void> mvnOption;
|
|
||||||
|
|
||||||
public CleanCommand() {
|
|
||||||
super("clean",
|
|
||||||
"Clean up groovy grapes (useful if snapshots are needed and you need an update)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUsageHelp() {
|
|
||||||
return "[options] <dependencies>";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected OptionParser createOptionParser() {
|
|
||||||
OptionParser parser = new OptionParser();
|
|
||||||
this.allOption = parser.accepts("all", "Clean all files (not just snapshots)");
|
|
||||||
this.ivyOption = parser.accepts("ivy",
|
|
||||||
"Clean just ivy (grapes) cache. Default is on unless --maven is used.");
|
|
||||||
this.mvnOption = parser.accepts("maven",
|
|
||||||
"Clean just maven cache. Default is off.");
|
|
||||||
return parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void run(OptionSet options) throws Exception {
|
|
||||||
if (!options.has(this.ivyOption)) {
|
|
||||||
clean(options, getGrapesHome(options), Layout.IVY);
|
|
||||||
}
|
|
||||||
if (options.has(this.mvnOption)) {
|
|
||||||
if (options.has(this.ivyOption)) {
|
|
||||||
clean(options, getGrapesHome(options), Layout.IVY);
|
|
||||||
}
|
|
||||||
clean(options, getMavenHome(options), Layout.MAVEN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clean(OptionSet options, File root, Layout layout) {
|
|
||||||
|
|
||||||
if (root == null || !root.exists()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<String> specs = new ArrayList<String>(options.nonOptionArguments());
|
|
||||||
if (!specs.contains("org.springframework.bootstrap") && layout == Layout.IVY) {
|
|
||||||
specs.add(0, "org.springframework.bootstrap");
|
|
||||||
}
|
|
||||||
for (String spec : specs) {
|
|
||||||
String group = spec;
|
|
||||||
String module = null;
|
|
||||||
if (spec.contains(":")) {
|
|
||||||
group = spec.substring(0, spec.indexOf(":"));
|
|
||||||
module = spec.substring(spec.indexOf(":") + 1);
|
|
||||||
}
|
|
||||||
File file = getModulePath(root, group, module, layout);
|
|
||||||
if (file.exists()) {
|
|
||||||
if (options.has(this.allOption)
|
|
||||||
|| group.equals("org.springframework.bootstrap")) {
|
|
||||||
System.out.println("Deleting: " + file);
|
|
||||||
FileUtil.forceDelete(file);
|
|
||||||
} else {
|
|
||||||
for (Object obj : FileUtil.listAll(file, Collections.emptyList())) {
|
|
||||||
File candidate = (File) obj;
|
|
||||||
if (candidate.getName().contains("SNAPSHOT")) {
|
|
||||||
System.out.println("Deleting: " + candidate);
|
|
||||||
FileUtil.forceDelete(candidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getModulePath(File root, String group, String module, Layout layout) {
|
|
||||||
File parent = root;
|
|
||||||
if (layout == Layout.IVY) {
|
|
||||||
parent = new File(parent, group);
|
|
||||||
} else {
|
|
||||||
for (String path : group.split("\\.")) {
|
|
||||||
parent = new File(parent, path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (module == null) {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
return new File(parent, module);
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getGrapesHome(OptionSet options) {
|
|
||||||
|
|
||||||
String dir = System.getenv("GROOVY_HOME");
|
|
||||||
String userdir = System.getProperty("user.home");
|
|
||||||
|
|
||||||
File home;
|
|
||||||
if (dir == null || !new File(dir).exists()) {
|
|
||||||
dir = userdir;
|
|
||||||
home = new File(dir, ".groovy");
|
|
||||||
} else {
|
|
||||||
home = new File(dir);
|
|
||||||
}
|
|
||||||
if (dir == null || !new File(dir).exists()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
File grapes = new File(home, "grapes");
|
|
||||||
return grapes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getMavenHome(OptionSet options) {
|
|
||||||
String dir = System.getProperty("user.home");
|
|
||||||
|
|
||||||
if (dir == null || !new File(dir).exists()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
File home = new File(dir);
|
|
||||||
File grapes = new File(new File(home, ".m2"), "repository");
|
|
||||||
return grapes;
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,16 +13,16 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.bootstrap.cli;
|
package org.springframework.bootstrap.cli;
|
||||||
|
|
||||||
/**
|
import java.util.Collection;
|
||||||
* Exception thrown when no CLI options are specified.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
*/
|
|
||||||
class NoArgumentsException extends BootstrapCliException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
/**
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface CommandFactory {
|
||||||
|
|
||||||
|
Collection<Command> getCommands();
|
||||||
|
|
||||||
}
|
}
|
@ -1,180 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.bootstrap.cli;
|
|
||||||
|
|
||||||
import java.awt.Desktop;
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
import joptsimple.OptionParser;
|
|
||||||
import joptsimple.OptionSet;
|
|
||||||
import joptsimple.OptionSpec;
|
|
||||||
|
|
||||||
import org.springframework.bootstrap.cli.runner.BootstrapRunner;
|
|
||||||
import org.springframework.bootstrap.cli.runner.BootstrapRunnerConfiguration;
|
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link Command} to 'run' a groovy script or scripts.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @author Dave Syer
|
|
||||||
*
|
|
||||||
* @see BootstrapRunner
|
|
||||||
*/
|
|
||||||
public class RunCommand extends OptionParsingCommand {
|
|
||||||
|
|
||||||
private OptionSpec<Void> watchOption;
|
|
||||||
|
|
||||||
private OptionSpec<Void> editOption;
|
|
||||||
|
|
||||||
private OptionSpec<Void> noGuessImportsOption;
|
|
||||||
|
|
||||||
private OptionSpec<Void> noGuessDependenciesOption;
|
|
||||||
|
|
||||||
private OptionSpec<Void> verboseOption;
|
|
||||||
|
|
||||||
private OptionSpec<Void> quietOption;
|
|
||||||
|
|
||||||
private OptionSpec<Void> localOption;
|
|
||||||
|
|
||||||
private BootstrapRunner runner;
|
|
||||||
|
|
||||||
public RunCommand() {
|
|
||||||
super("run", "Run a spring groovy script");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUsageHelp() {
|
|
||||||
return "[options] <files> [--] [args]";
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stop() {
|
|
||||||
if (this.runner != null) {
|
|
||||||
this.runner.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected OptionParser createOptionParser() {
|
|
||||||
OptionParser parser = new OptionParser();
|
|
||||||
this.watchOption = parser
|
|
||||||
.accepts("watch", "Watch the specified file for changes");
|
|
||||||
this.localOption = parser.accepts("local",
|
|
||||||
"Accumulate the dependencies in a local folder (./grapes)");
|
|
||||||
this.editOption = parser.acceptsAll(asList("edit", "e"),
|
|
||||||
"Open the file with the default system editor");
|
|
||||||
this.noGuessImportsOption = parser.accepts("no-guess-imports",
|
|
||||||
"Do not attempt to guess imports");
|
|
||||||
this.noGuessDependenciesOption = parser.accepts("no-guess-dependencies",
|
|
||||||
"Do not attempt to guess dependencies");
|
|
||||||
this.verboseOption = parser.acceptsAll(asList("verbose", "v"), "Verbose logging");
|
|
||||||
this.quietOption = parser.acceptsAll(asList("quiet", "q"), "Quiet logging");
|
|
||||||
return parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void run(OptionSet options) throws Exception {
|
|
||||||
List<String> nonOptionArguments = options.nonOptionArguments();
|
|
||||||
File[] files = getFileArguments(nonOptionArguments);
|
|
||||||
List<String> args = nonOptionArguments.subList(files.length,
|
|
||||||
nonOptionArguments.size());
|
|
||||||
|
|
||||||
if (options.has(this.editOption)) {
|
|
||||||
Desktop.getDesktop().edit(files[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
BootstrapRunnerConfiguration configuration = new BootstrapRunnerConfigurationAdapter(
|
|
||||||
options);
|
|
||||||
if (configuration.isLocal() && System.getProperty("grape.root") == null) {
|
|
||||||
System.setProperty("grape.root", ".");
|
|
||||||
}
|
|
||||||
this.runner = new BootstrapRunner(configuration, files,
|
|
||||||
args.toArray(new String[args.size()]));
|
|
||||||
this.runner.compileAndRun();
|
|
||||||
}
|
|
||||||
|
|
||||||
private File[] getFileArguments(List<String> nonOptionArguments) {
|
|
||||||
List<File> files = new ArrayList<File>();
|
|
||||||
for (String filename : nonOptionArguments) {
|
|
||||||
if ("--".equals(filename)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// TODO: add support for strict Java compilation
|
|
||||||
// TODO: add support for recursive search in directory
|
|
||||||
if (filename.endsWith(".groovy") || filename.endsWith(".java")) {
|
|
||||||
File file = new File(filename);
|
|
||||||
if (file.isFile() && file.canRead()) {
|
|
||||||
files.add(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (files.size() == 0) {
|
|
||||||
throw new RuntimeException("Please specify a file to run");
|
|
||||||
}
|
|
||||||
return files.toArray(new File[files.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple adapter class to present the {@link OptionSet} as a
|
|
||||||
* {@link BootstrapRunnerConfiguration}.
|
|
||||||
*/
|
|
||||||
private class BootstrapRunnerConfigurationAdapter implements
|
|
||||||
BootstrapRunnerConfiguration {
|
|
||||||
|
|
||||||
private OptionSet options;
|
|
||||||
|
|
||||||
public BootstrapRunnerConfigurationAdapter(OptionSet options) {
|
|
||||||
this.options = options;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isWatchForFileChanges() {
|
|
||||||
return this.options.has(RunCommand.this.watchOption);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isGuessImports() {
|
|
||||||
return !this.options.has(RunCommand.this.noGuessImportsOption);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isGuessDependencies() {
|
|
||||||
return !this.options.has(RunCommand.this.noGuessDependenciesOption);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLocal() {
|
|
||||||
return this.options.has(RunCommand.this.localOption);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Level getLogLevel() {
|
|
||||||
if (this.options.has(RunCommand.this.verboseOption)) {
|
|
||||||
return Level.FINEST;
|
|
||||||
}
|
|
||||||
if (this.options.has(RunCommand.this.quietOption)) {
|
|
||||||
return Level.OFF;
|
|
||||||
}
|
|
||||||
return Level.INFO;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,10 +16,13 @@
|
|||||||
|
|
||||||
package org.springframework.bootstrap.cli;
|
package org.springframework.bootstrap.cli;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,14 +32,12 @@ import java.util.Set;
|
|||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* The '-d' and '--debug' switches are handled by this class, however, most argument
|
* The '-d' and '--debug' switches are handled by this class, however, most argument
|
||||||
* parsing is left to the {@link Command} implementation. The {@link OptionParsingCommand}
|
* parsing is left to the {@link Command} implementation.
|
||||||
* class provides a convenient base for command that need to parse arguments.
|
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @see #main(String...)
|
* @see #main(String...)
|
||||||
* @see BootstrapCliException
|
* @see BootstrapCliException
|
||||||
* @see Command
|
* @see Command
|
||||||
* @see OptionParsingCommand
|
|
||||||
*/
|
*/
|
||||||
public class SpringBootstrapCli {
|
public class SpringBootstrapCli {
|
||||||
|
|
||||||
@ -52,8 +53,17 @@ public class SpringBootstrapCli {
|
|||||||
* commands.
|
* commands.
|
||||||
*/
|
*/
|
||||||
public SpringBootstrapCli() {
|
public SpringBootstrapCli() {
|
||||||
setCommands(Arrays.asList(new VersionCommand(), new RunCommand(),
|
setCommands(ServiceLoader.load(CommandFactory.class, getClass().getClassLoader()));
|
||||||
new CreateCommand(), new CleanCommand()));
|
}
|
||||||
|
|
||||||
|
private void setCommands(Iterable<CommandFactory> iterable) {
|
||||||
|
this.commands = new ArrayList<Command>();
|
||||||
|
for (CommandFactory factory : iterable) {
|
||||||
|
for (Command command : factory.getCommands()) {
|
||||||
|
this.commands.add(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.commands.add(0, new HelpCommand());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,7 +71,7 @@ public class SpringBootstrapCli {
|
|||||||
* 'help' command will be automatically provided in addition to this list.
|
* 'help' command will be automatically provided in addition to this list.
|
||||||
* @param commands the commands to add
|
* @param commands the commands to add
|
||||||
*/
|
*/
|
||||||
protected void setCommands(List<? extends Command> commands) {
|
public void setCommands(List<? extends Command> commands) {
|
||||||
this.commands = new ArrayList<Command>(commands);
|
this.commands = new ArrayList<Command>(commands);
|
||||||
this.commands.add(0, new HelpCommand());
|
this.commands.add(0, new HelpCommand());
|
||||||
}
|
}
|
||||||
@ -172,11 +182,7 @@ public class SpringBootstrapCli {
|
|||||||
/**
|
/**
|
||||||
* Internal {@link Command} used for 'help' and '--help' requests.
|
* Internal {@link Command} used for 'help' and '--help' requests.
|
||||||
*/
|
*/
|
||||||
private class HelpCommand extends AbstractCommand {
|
private class HelpCommand implements Command {
|
||||||
|
|
||||||
public HelpCommand() {
|
|
||||||
super("help", "Show command help", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(String... args) throws Exception {
|
public void run(String... args) throws Exception {
|
||||||
@ -201,6 +207,46 @@ public class SpringBootstrapCli {
|
|||||||
throw new NoSuchCommandException(commandName);
|
throw new NoSuchCommandException(commandName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "help";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOptionCommand() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Get help on commands";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsageHelp() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void printHelp(PrintStream out) throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class NoHelpCommandArgumentsException extends BootstrapCliException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public NoHelpCommandArgumentsException() {
|
||||||
|
super(Option.SHOW_USAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class NoArgumentsException extends BootstrapCliException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,11 +14,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.bootstrap.cli;
|
package org.springframework.bootstrap.cli.command;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
import org.springframework.bootstrap.cli.Command;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract {@link Command} implementation.
|
* Abstract {@link Command} implementation.
|
||||||
*
|
*
|
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* 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.bootstrap.cli.command;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import joptsimple.OptionSet;
|
||||||
|
import joptsimple.OptionSpec;
|
||||||
|
|
||||||
|
import org.apache.ivy.util.FileUtil;
|
||||||
|
import org.springframework.bootstrap.cli.Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Command} to 'clean' up grapes, removing cached dependencies and forcing a
|
||||||
|
* download on the next attempt to resolve.
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class CleanCommand extends OptionParsingCommand {
|
||||||
|
|
||||||
|
public CleanCommand() {
|
||||||
|
super(
|
||||||
|
"clean",
|
||||||
|
"Clean up groovy grapes (useful if snapshots are needed and you need an update)",
|
||||||
|
new CleanOptionHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsageHelp() {
|
||||||
|
return "[options] <dependencies>";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CleanOptionHandler extends OptionHandler {
|
||||||
|
|
||||||
|
private static enum Layout {
|
||||||
|
IVY, MAVEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OptionSpec<Void> allOption;
|
||||||
|
|
||||||
|
private OptionSpec<Void> ivyOption;
|
||||||
|
|
||||||
|
private OptionSpec<Void> mvnOption;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void options() {
|
||||||
|
this.allOption = option("all", "Clean all files (not just snapshots)");
|
||||||
|
this.ivyOption = option("ivy",
|
||||||
|
"Clean just ivy (grapes) cache. Default is on unless --maven is used.");
|
||||||
|
this.mvnOption = option("maven", "Clean just maven cache. Default is off.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run(OptionSet options) throws Exception {
|
||||||
|
if (!options.has(this.ivyOption)) {
|
||||||
|
clean(options, getGrapesHome(options), Layout.IVY);
|
||||||
|
}
|
||||||
|
if (options.has(this.mvnOption)) {
|
||||||
|
if (options.has(this.ivyOption)) {
|
||||||
|
clean(options, getGrapesHome(options), Layout.IVY);
|
||||||
|
}
|
||||||
|
clean(options, getMavenHome(options), Layout.MAVEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clean(OptionSet options, File root, Layout layout) {
|
||||||
|
|
||||||
|
if (root == null || !root.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<String> specs = new ArrayList<String>(options.nonOptionArguments());
|
||||||
|
if (!specs.contains("org.springframework.bootstrap") && layout == Layout.IVY) {
|
||||||
|
specs.add(0, "org.springframework.bootstrap");
|
||||||
|
}
|
||||||
|
for (String spec : specs) {
|
||||||
|
String group = spec;
|
||||||
|
String module = null;
|
||||||
|
if (spec.contains(":")) {
|
||||||
|
group = spec.substring(0, spec.indexOf(":"));
|
||||||
|
module = spec.substring(spec.indexOf(":") + 1);
|
||||||
|
}
|
||||||
|
File file = getModulePath(root, group, module, layout);
|
||||||
|
if (file.exists()) {
|
||||||
|
if (options.has(this.allOption)
|
||||||
|
|| group.equals("org.springframework.bootstrap")) {
|
||||||
|
System.out.println("Deleting: " + file);
|
||||||
|
FileUtil.forceDelete(file);
|
||||||
|
} else {
|
||||||
|
for (Object obj : FileUtil.listAll(file, Collections.emptyList())) {
|
||||||
|
File candidate = (File) obj;
|
||||||
|
if (candidate.getName().contains("SNAPSHOT")) {
|
||||||
|
System.out.println("Deleting: " + candidate);
|
||||||
|
FileUtil.forceDelete(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getModulePath(File root, String group, String module, Layout layout) {
|
||||||
|
File parent = root;
|
||||||
|
if (layout == Layout.IVY) {
|
||||||
|
parent = new File(parent, group);
|
||||||
|
} else {
|
||||||
|
for (String path : group.split("\\.")) {
|
||||||
|
parent = new File(parent, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (module == null) {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
return new File(parent, module);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getGrapesHome(OptionSet options) {
|
||||||
|
|
||||||
|
String dir = System.getenv("GROOVY_HOME");
|
||||||
|
String userdir = System.getProperty("user.home");
|
||||||
|
|
||||||
|
File home;
|
||||||
|
if (dir == null || !new File(dir).exists()) {
|
||||||
|
dir = userdir;
|
||||||
|
home = new File(dir, ".groovy");
|
||||||
|
} else {
|
||||||
|
home = new File(dir);
|
||||||
|
}
|
||||||
|
if (dir == null || !new File(dir).exists()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
File grapes = new File(home, "grapes");
|
||||||
|
return grapes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getMavenHome(OptionSet options) {
|
||||||
|
String dir = System.getProperty("user.home");
|
||||||
|
|
||||||
|
if (dir == null || !new File(dir).exists()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
File home = new File(dir);
|
||||||
|
File grapes = new File(new File(home, ".m2"), "repository");
|
||||||
|
return grapes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -14,12 +14,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.bootstrap.cli;
|
package org.springframework.bootstrap.cli.command;
|
||||||
|
|
||||||
import joptsimple.OptionParser;
|
|
||||||
import joptsimple.OptionSet;
|
import joptsimple.OptionSet;
|
||||||
|
|
||||||
import static java.util.Arrays.*;
|
import org.springframework.bootstrap.cli.Command;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Command} to 'create' a new spring groovy script.
|
* {@link Command} to 'create' a new spring groovy script.
|
||||||
@ -29,7 +30,7 @@ import static java.util.Arrays.*;
|
|||||||
public class CreateCommand extends OptionParsingCommand {
|
public class CreateCommand extends OptionParsingCommand {
|
||||||
|
|
||||||
public CreateCommand() {
|
public CreateCommand() {
|
||||||
super("create", "Create an new spring groovy script");
|
super("create", "Create an new spring groovy script", new CreateOptionHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -37,13 +38,13 @@ public class CreateCommand extends OptionParsingCommand {
|
|||||||
return "[options] <file>";
|
return "[options] <file>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class CreateOptionHandler extends OptionHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected OptionParser createOptionParser() {
|
protected void options() {
|
||||||
OptionParser parser = new OptionParser();
|
option(asList("overwite", "f"), "Overwrite any existing file");
|
||||||
parser.acceptsAll(asList("overwite", "f"), "Overwrite any existing file");
|
option("type", "Create a specific application type").withOptionalArg()
|
||||||
parser.accepts("type", "Create a specific application type").withOptionalArg()
|
|
||||||
.ofType(String.class).describedAs("web, batch, integration");
|
.ofType(String.class).describedAs("web, batch, integration");
|
||||||
return parser;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -51,4 +52,6 @@ public class CreateCommand extends OptionParsingCommand {
|
|||||||
throw new IllegalStateException("Not implemented"); // FIXME
|
throw new IllegalStateException("Not implemented"); // FIXME
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -13,20 +13,27 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
package org.springframework.bootstrap.cli.command;
|
||||||
|
|
||||||
package org.springframework.bootstrap.cli;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.bootstrap.cli.Command;
|
||||||
|
import org.springframework.bootstrap.cli.CommandFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception thrown when the 'help' command is issued without any arguments.
|
* @author Dave Syer
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
|
||||||
*/
|
*/
|
||||||
class NoHelpCommandArgumentsException extends BootstrapCliException {
|
public class DefaultCommandFactory implements CommandFactory {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final List<Command> DEFAULT_COMMANDS = Arrays.<Command> asList(
|
||||||
|
new VersionCommand(), new RunCommand(), new CleanCommand());
|
||||||
|
|
||||||
public NoHelpCommandArgumentsException() {
|
@Override
|
||||||
super(Option.SHOW_USAGE);
|
public Collection<Command> getCommands() {
|
||||||
|
return DEFAULT_COMMANDS;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* 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.bootstrap.cli.command;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import joptsimple.OptionParser;
|
||||||
|
import joptsimple.OptionSet;
|
||||||
|
import joptsimple.OptionSpecBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class OptionHandler {
|
||||||
|
|
||||||
|
private OptionParser parser;
|
||||||
|
|
||||||
|
public OptionSpecBuilder option(String name, String description) {
|
||||||
|
return getParser().accepts(name, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionSpecBuilder option(Collection<String> aliases, String description) {
|
||||||
|
return getParser().acceptsAll(aliases, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OptionParser getParser() {
|
||||||
|
if (this.parser == null) {
|
||||||
|
this.parser = new OptionParser();
|
||||||
|
options();
|
||||||
|
}
|
||||||
|
return this.parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void options();
|
||||||
|
|
||||||
|
public final void run(String... args) throws Exception {
|
||||||
|
OptionSet options = getParser().parse(args);
|
||||||
|
run(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void run(OptionSet options) throws Exception;
|
||||||
|
|
||||||
|
public void printHelp(PrintStream out) throws IOException {
|
||||||
|
getParser().printHelpOn(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -14,13 +14,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.bootstrap.cli;
|
package org.springframework.bootstrap.cli.command;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
|
|
||||||
import joptsimple.OptionParser;
|
import joptsimple.OptionParser;
|
||||||
import joptsimple.OptionSet;
|
|
||||||
|
import org.springframework.bootstrap.cli.Command;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for any {@link Command}s that use an {@link OptionParser}.
|
* Base class for any {@link Command}s that use an {@link OptionParser}.
|
||||||
@ -29,26 +30,25 @@ import joptsimple.OptionSet;
|
|||||||
*/
|
*/
|
||||||
public abstract class OptionParsingCommand extends AbstractCommand {
|
public abstract class OptionParsingCommand extends AbstractCommand {
|
||||||
|
|
||||||
private OptionParser parser;
|
private OptionHandler handler;
|
||||||
|
|
||||||
public OptionParsingCommand(String name, String description) {
|
public OptionParsingCommand(String name, String description, OptionHandler handler) {
|
||||||
super(name, description);
|
super(name, description);
|
||||||
this.parser = createOptionParser();
|
this.handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract OptionParser createOptionParser();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void printHelp(PrintStream out) throws IOException {
|
public void printHelp(PrintStream out) throws IOException {
|
||||||
this.parser.printHelpOn(out);
|
this.handler.printHelp(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void run(String... args) throws Exception {
|
public final void run(String... args) throws Exception {
|
||||||
OptionSet options = parser.parse(args);
|
this.handler.run(args);
|
||||||
run(options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void run(OptionSet options) throws Exception;
|
protected OptionHandler getHandler() {
|
||||||
|
return this.handler;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* 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.bootstrap.cli.command;
|
||||||
|
|
||||||
|
import java.awt.Desktop;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import joptsimple.OptionSet;
|
||||||
|
import joptsimple.OptionSpec;
|
||||||
|
|
||||||
|
import org.springframework.bootstrap.cli.Command;
|
||||||
|
import org.springframework.bootstrap.cli.runner.BootstrapRunner;
|
||||||
|
import org.springframework.bootstrap.cli.runner.BootstrapRunnerConfiguration;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Command} to 'run' a groovy script or scripts.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
* @see BootstrapRunner
|
||||||
|
*/
|
||||||
|
public class RunCommand extends OptionParsingCommand {
|
||||||
|
|
||||||
|
public RunCommand() {
|
||||||
|
super("run", "Run a spring groovy script", new RunOptionHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsageHelp() {
|
||||||
|
return "[options] <files> [--] [args]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
if (this.getHandler() != null) {
|
||||||
|
((RunOptionHandler) this.getHandler()).runner.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class RunOptionHandler extends OptionHandler {
|
||||||
|
|
||||||
|
private OptionSpec<Void> watchOption;
|
||||||
|
|
||||||
|
private OptionSpec<Void> editOption;
|
||||||
|
|
||||||
|
private OptionSpec<Void> noGuessImportsOption;
|
||||||
|
|
||||||
|
private OptionSpec<Void> noGuessDependenciesOption;
|
||||||
|
|
||||||
|
private OptionSpec<Void> verboseOption;
|
||||||
|
|
||||||
|
private OptionSpec<Void> quietOption;
|
||||||
|
|
||||||
|
private OptionSpec<Void> localOption;
|
||||||
|
|
||||||
|
private BootstrapRunner runner;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void options() {
|
||||||
|
this.watchOption = option("watch", "Watch the specified file for changes");
|
||||||
|
this.localOption = option("local",
|
||||||
|
"Accumulate the dependencies in a local folder (./grapes)");
|
||||||
|
this.editOption = option(asList("edit", "e"),
|
||||||
|
"Open the file with the default system editor");
|
||||||
|
this.noGuessImportsOption = option("no-guess-imports",
|
||||||
|
"Do not attempt to guess imports");
|
||||||
|
this.noGuessDependenciesOption = option("no-guess-dependencies",
|
||||||
|
"Do not attempt to guess dependencies");
|
||||||
|
this.verboseOption = option(asList("verbose", "v"), "Verbose logging");
|
||||||
|
this.quietOption = option(asList("quiet", "q"), "Quiet logging");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run(OptionSet options) throws Exception {
|
||||||
|
List<String> nonOptionArguments = options.nonOptionArguments();
|
||||||
|
File[] files = getFileArguments(nonOptionArguments);
|
||||||
|
List<String> args = nonOptionArguments.subList(files.length,
|
||||||
|
nonOptionArguments.size());
|
||||||
|
|
||||||
|
if (options.has(this.editOption)) {
|
||||||
|
Desktop.getDesktop().edit(files[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
BootstrapRunnerConfiguration configuration = new BootstrapRunnerConfigurationAdapter(
|
||||||
|
options);
|
||||||
|
if (configuration.isLocal() && System.getProperty("grape.root") == null) {
|
||||||
|
System.setProperty("grape.root", ".");
|
||||||
|
}
|
||||||
|
this.runner = new BootstrapRunner(configuration, files,
|
||||||
|
args.toArray(new String[args.size()]));
|
||||||
|
this.runner.compileAndRun();
|
||||||
|
}
|
||||||
|
|
||||||
|
private File[] getFileArguments(List<String> nonOptionArguments) {
|
||||||
|
List<File> files = new ArrayList<File>();
|
||||||
|
for (String filename : nonOptionArguments) {
|
||||||
|
if ("--".equals(filename)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// TODO: add support for strict Java compilation
|
||||||
|
// TODO: add support for recursive search in directory
|
||||||
|
if (filename.endsWith(".groovy") || filename.endsWith(".java")) {
|
||||||
|
File file = new File(filename);
|
||||||
|
if (file.isFile() && file.canRead()) {
|
||||||
|
files.add(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (files.size() == 0) {
|
||||||
|
throw new RuntimeException("Please specify a file to run");
|
||||||
|
}
|
||||||
|
return files.toArray(new File[files.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple adapter class to present the {@link OptionSet} as a
|
||||||
|
* {@link BootstrapRunnerConfiguration}.
|
||||||
|
*/
|
||||||
|
private class BootstrapRunnerConfigurationAdapter implements
|
||||||
|
BootstrapRunnerConfiguration {
|
||||||
|
|
||||||
|
private OptionSet options;
|
||||||
|
|
||||||
|
public BootstrapRunnerConfigurationAdapter(OptionSet options) {
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWatchForFileChanges() {
|
||||||
|
return this.options.has(RunOptionHandler.this.watchOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGuessImports() {
|
||||||
|
return !this.options.has(RunOptionHandler.this.noGuessImportsOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGuessDependencies() {
|
||||||
|
return !this.options.has(RunOptionHandler.this.noGuessDependenciesOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLocal() {
|
||||||
|
return this.options.has(RunOptionHandler.this.localOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Level getLogLevel() {
|
||||||
|
if (this.options.has(RunOptionHandler.this.verboseOption)) {
|
||||||
|
return Level.FINEST;
|
||||||
|
}
|
||||||
|
if (this.options.has(RunOptionHandler.this.quietOption)) {
|
||||||
|
return Level.OFF;
|
||||||
|
}
|
||||||
|
return Level.INFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* 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.bootstrap.cli.command;
|
||||||
|
|
||||||
|
import groovy.lang.Script;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import org.apache.ivy.util.FileUtil;
|
||||||
|
import org.codehaus.groovy.control.CompilationFailedException;
|
||||||
|
import org.springframework.bootstrap.cli.Command;
|
||||||
|
import org.springframework.bootstrap.cli.compiler.GroovyCompiler;
|
||||||
|
import org.springframework.bootstrap.cli.compiler.GroovyCompilerConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Command} to run a script.
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ScriptCommand extends AbstractCommand {
|
||||||
|
|
||||||
|
private static String[] DEFAULT_PATHS = new String[] { "${SPRING_HOME}/ext",
|
||||||
|
"${SPRING_HOME}/bin" };
|
||||||
|
|
||||||
|
private String[] paths = DEFAULT_PATHS;
|
||||||
|
|
||||||
|
private Class<?> mainClass;
|
||||||
|
|
||||||
|
private Object main;
|
||||||
|
|
||||||
|
public ScriptCommand(String name, String description) {
|
||||||
|
super(name, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void printHelp(PrintStream out) throws IOException {
|
||||||
|
if (getMain() instanceof OptionHandler) {
|
||||||
|
((OptionHandler) getMain()).printHelp(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) throws Exception {
|
||||||
|
if (getMain() instanceof OptionHandler) {
|
||||||
|
((OptionHandler) getMain()).run(args);
|
||||||
|
} else if (this.main instanceof Runnable) {
|
||||||
|
((Runnable) this.main).run();
|
||||||
|
} else if (this.main instanceof Script) {
|
||||||
|
Script script = (Script) this.main;
|
||||||
|
script.setProperty("args", args);
|
||||||
|
script.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paths to search for script files.
|
||||||
|
*
|
||||||
|
* @param paths the paths to set
|
||||||
|
*/
|
||||||
|
public void setPaths(String[] paths) {
|
||||||
|
this.paths = paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsageHelp() {
|
||||||
|
return "[options] <args>";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object getMain() {
|
||||||
|
if (this.main == null) {
|
||||||
|
try {
|
||||||
|
this.main = getMainClass().newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException("Cannot create main class: " + getName(),
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.main;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void compile() {
|
||||||
|
GroovyCompiler compiler = new GroovyCompiler(new ScriptConfiguration());
|
||||||
|
compiler.addCompilationCustomizers(new ScriptCompilationCustomizer());
|
||||||
|
File source = locateSource(getName());
|
||||||
|
Class<?>[] classes;
|
||||||
|
try {
|
||||||
|
classes = compiler.compile(source);
|
||||||
|
} catch (CompilationFailedException e) {
|
||||||
|
throw new IllegalStateException("Could not compile script", e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException("Could not compile script", e);
|
||||||
|
}
|
||||||
|
this.mainClass = classes[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> getMainClass() {
|
||||||
|
if (this.mainClass == null) {
|
||||||
|
compile();
|
||||||
|
}
|
||||||
|
return this.mainClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File locateSource(String name) {
|
||||||
|
String resource = "commands/" + name + ".groovy";
|
||||||
|
URL url = getClass().getClassLoader().getResource(resource);
|
||||||
|
File file = null;
|
||||||
|
if (url != null) {
|
||||||
|
try {
|
||||||
|
file = File.createTempFile(name, ".groovy");
|
||||||
|
FileUtil.copy(url, file, null);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException("Could not create temp file for source: "
|
||||||
|
+ name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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 test = new File(subbed, resource);
|
||||||
|
if (test.exists()) {
|
||||||
|
file = test;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (file == null) {
|
||||||
|
throw new IllegalStateException("No script found for : " + name);
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ScriptConfiguration implements GroovyCompilerConfiguration {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGuessImports() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGuessDependencies() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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.bootstrap.cli.command;
|
||||||
|
|
||||||
|
import org.codehaus.groovy.ast.ClassNode;
|
||||||
|
import org.codehaus.groovy.classgen.GeneratorContext;
|
||||||
|
import org.codehaus.groovy.control.CompilationFailedException;
|
||||||
|
import org.codehaus.groovy.control.CompilePhase;
|
||||||
|
import org.codehaus.groovy.control.SourceUnit;
|
||||||
|
import org.codehaus.groovy.control.customizers.CompilationCustomizer;
|
||||||
|
import org.codehaus.groovy.control.customizers.ImportCustomizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ScriptCompilationCustomizer extends CompilationCustomizer {
|
||||||
|
|
||||||
|
public ScriptCompilationCustomizer() {
|
||||||
|
super(CompilePhase.CONVERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void call(SourceUnit source, GeneratorContext context, ClassNode classNode)
|
||||||
|
throws CompilationFailedException {
|
||||||
|
// AnnotationNode mixin = new AnnotationNode(ClassHelper.make(Mixin.class));
|
||||||
|
// mixin.addMember("value",
|
||||||
|
// new ClassExpression(ClassHelper.make(OptionHandler.class)));
|
||||||
|
// classNode.addAnnotation(mixin);
|
||||||
|
ImportCustomizer importCustomizer = new ImportCustomizer();
|
||||||
|
importCustomizer.addImports("joptsimple.OptionParser", "joptsimple.OptionSet",
|
||||||
|
OptionParsingCommand.class.getCanonicalName(),
|
||||||
|
OptionHandler.class.getCanonicalName());
|
||||||
|
importCustomizer.call(source, context, classNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -14,7 +14,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.bootstrap.cli;
|
package org.springframework.bootstrap.cli.command;
|
||||||
|
|
||||||
|
import org.springframework.bootstrap.cli.Command;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Command} to display the 'version' number.
|
* {@link Command} to display the 'version' number.
|
@ -72,15 +72,17 @@ public class GroovyCompiler {
|
|||||||
CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
|
CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
|
||||||
this.loader = new ExtendedGroovyClassLoader(getClass().getClassLoader(),
|
this.loader = new ExtendedGroovyClassLoader(getClass().getClassLoader(),
|
||||||
compilerConfiguration);
|
compilerConfiguration);
|
||||||
|
|
||||||
// FIXME: allow the extra resolvers to be switched on (off by default)
|
// FIXME: allow the extra resolvers to be switched on (off by default)
|
||||||
addExtraResolvers();
|
addExtraResolvers();
|
||||||
|
|
||||||
compilerConfiguration
|
compilerConfiguration
|
||||||
.addCompilationCustomizers(new CompilerAutoConfigureCustomizer());
|
.addCompilationCustomizers(new CompilerAutoConfigureCustomizer());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object[] sources(File[] files) throws CompilationFailedException, IOException {
|
public void addCompilationCustomizers(CompilationCustomizer... customizers) {
|
||||||
|
this.loader.getConfiguration().addCompilationCustomizers(customizers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object[] sources(File... files) throws CompilationFailedException, IOException {
|
||||||
List<File> compilables = new ArrayList<File>();
|
List<File> compilables = new ArrayList<File>();
|
||||||
List<Object> others = new ArrayList<Object>();
|
List<Object> others = new ArrayList<Object>();
|
||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
@ -125,6 +127,18 @@ public class GroovyCompiler {
|
|||||||
for (Object loadedClass : collector.getLoadedClasses()) {
|
for (Object loadedClass : collector.getLoadedClasses()) {
|
||||||
classes.add((Class<?>) loadedClass);
|
classes.add((Class<?>) loadedClass);
|
||||||
}
|
}
|
||||||
|
ClassNode mainClassNode = (ClassNode) compilationUnit.getAST().getClasses()
|
||||||
|
.get(0);
|
||||||
|
Class<?> mainClass = null;
|
||||||
|
for (Class<?> loadedClass : classes) {
|
||||||
|
if (mainClassNode.getName().equals(loadedClass.getName())) {
|
||||||
|
mainClass = loadedClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mainClass != null) {
|
||||||
|
classes.remove(mainClass);
|
||||||
|
classes.add(0, mainClass);
|
||||||
|
}
|
||||||
|
|
||||||
return classes.toArray(new Class<?>[classes.size()]);
|
return classes.toArray(new Class<?>[classes.size()]);
|
||||||
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
org.springframework.bootstrap.cli.command.DefaultCommandFactory
|
@ -28,6 +28,7 @@ import org.junit.After;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.bootstrap.cli.command.RunCommand;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
@ -11,6 +11,8 @@ import org.junit.Test;
|
|||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.springframework.bootstrap.cli.SpringBootstrapCli.NoArgumentsException;
|
||||||
|
import org.springframework.bootstrap.cli.SpringBootstrapCli.NoHelpCommandArgumentsException;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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.bootstrap.cli.command;
|
||||||
|
|
||||||
|
import groovy.lang.Script;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ScriptCommandTests {
|
||||||
|
|
||||||
|
public static boolean executed = false;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testScript() throws Exception {
|
||||||
|
ScriptCommand command = new ScriptCommand("script", "Run a test command");
|
||||||
|
command.run("World");
|
||||||
|
assertEquals("World",
|
||||||
|
((String[]) ((Script) command.getMain()).getProperty("args"))[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOptions() throws Exception {
|
||||||
|
ScriptCommand command = new ScriptCommand("test", "Run a test command");
|
||||||
|
command.run("World", "--foo");
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
((OptionHandler) command.getMain()).printHelp(new PrintStream(out));
|
||||||
|
assertTrue("Wrong output: " + out, out.toString().contains("--foo"));
|
||||||
|
assertTrue(executed);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
println "Hello ${args[0]}"
|
19
spring-bootstrap-cli/src/test/resources/commands/test.groovy
Normal file
19
spring-bootstrap-cli/src/test/resources/commands/test.groovy
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package org.test.command
|
||||||
|
|
||||||
|
@Grab("org.eclipse.jgit:org.eclipse.jgit:2.3.1.201302201838-r")
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
|
||||||
|
class TestCommand extends OptionHandler {
|
||||||
|
|
||||||
|
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()
|
||||||
|
org.springframework.bootstrap.cli.command.ScriptCommandTests.executed = true
|
||||||
|
println "Hello ${options.nonOptionArguments()}: ${options.has('foo')}"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user