Change package names zero->boot
* actuator -> boot-ops * cli -> boot-cli * launcher -> boot-load * autoconfig -> boot-config * bootstrap -> boot-strap * starters -> boot-up [#54095231] [bs-253] Refactor Zero->Boot
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* A single command that can be run from the CLI.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @see #run(String...)
|
||||
*/
|
||||
public interface Command {
|
||||
|
||||
/**
|
||||
* Returns the name of the command.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Returns a description of the command.
|
||||
*/
|
||||
String getDescription();
|
||||
|
||||
/**
|
||||
* Returns usage help for the command. This should be a simple one-line string
|
||||
* describing basic usage. e.g. '[options] <file>'. Do not include the name of
|
||||
* the command in this string.
|
||||
*/
|
||||
String getUsageHelp();
|
||||
|
||||
/**
|
||||
* Gets full help text for the command, e.g. a longer description and one line per
|
||||
* option.
|
||||
*/
|
||||
String getHelp();
|
||||
|
||||
/**
|
||||
* Run the command.
|
||||
* @param args command arguments (this will not include the command itself)
|
||||
* @throws Exception
|
||||
*/
|
||||
void run(String... args) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* Factory used to create CLI {@link Command}s. Intended for use with a Java
|
||||
* {@link ServiceLoader}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public interface CommandFactory {
|
||||
|
||||
/**
|
||||
* Returns the CLI {@link Command}s.
|
||||
* @return The commands
|
||||
*/
|
||||
Collection<Command> getCommands();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Simple logger used by the CLI.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public abstract class Log {
|
||||
|
||||
public static void info(String message) {
|
||||
System.out.println(message);
|
||||
}
|
||||
|
||||
public static void error(String message) {
|
||||
System.err.println(message);
|
||||
}
|
||||
|
||||
public static void error(Exception ex) {
|
||||
ex.printStackTrace(System.err);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Exception thrown when an unknown command is specified.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class NoSuchCommandException extends SpringCliException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public NoSuchCommandException(String name) {
|
||||
super(String.format("%1$s: '%2$s' is not a valid command. See '%1$s help'.",
|
||||
SpringCli.CLI_APP, name));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Spring Command Line Interface. This is the main entry-point for the Spring command line
|
||||
* application. This class will parse input arguments and delegate to a suitable
|
||||
* {@link Command} implementation based on the first argument.
|
||||
*
|
||||
* <p>
|
||||
* The '-d' and '--debug' switches are handled by this class, however, most argument
|
||||
* parsing is left to the {@link Command} implementation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see #main(String...)
|
||||
* @see SpringCliException
|
||||
* @see Command
|
||||
*/
|
||||
public class SpringCli {
|
||||
|
||||
public static final String CLI_APP = "spring";
|
||||
|
||||
private static final Set<SpringCliException.Option> NO_EXCEPTION_OPTIONS = EnumSet
|
||||
.noneOf(SpringCliException.Option.class);
|
||||
|
||||
private List<Command> commands;
|
||||
|
||||
/**
|
||||
* Create a new {@link SpringCli} implementation with the default set of commands.
|
||||
*/
|
||||
public SpringCli() {
|
||||
setCommands(ServiceLoader.load(CommandFactory.class, getClass().getClassLoader()));
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param commands the commands to add
|
||||
*/
|
||||
public void setCommands(List<? extends Command> commands) {
|
||||
this.commands = new ArrayList<Command>(commands);
|
||||
this.commands.add(0, new HelpCommand());
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the CLI and handle and errors.
|
||||
* @param args the input arguments
|
||||
* @return a return status code (non boot is used to indicate an error)
|
||||
*/
|
||||
public int runAndHandleErrors(String... args) {
|
||||
String[] argsWithoutDebugFlags = removeDebugFlags(args);
|
||||
boolean debug = argsWithoutDebugFlags.length != args.length;
|
||||
try {
|
||||
run(argsWithoutDebugFlags);
|
||||
return 0;
|
||||
}
|
||||
catch (NoArgumentsException ex) {
|
||||
showUsage();
|
||||
return 1;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return handleError(debug, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private int handleError(boolean debug, Exception ex) {
|
||||
Set<SpringCliException.Option> options = NO_EXCEPTION_OPTIONS;
|
||||
if (ex instanceof SpringCliException) {
|
||||
options = ((SpringCliException) ex).getOptions();
|
||||
}
|
||||
if (!(ex instanceof NoHelpCommandArgumentsException)) {
|
||||
errorMessage(ex.getMessage());
|
||||
}
|
||||
if (options.contains(SpringCliException.Option.SHOW_USAGE)) {
|
||||
showUsage();
|
||||
}
|
||||
if (debug || options.contains(SpringCliException.Option.STACK_TRACE)) {
|
||||
printStackTrace(ex);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the arguments and run a suitable command.
|
||||
* @param args the arguments
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void run(String... args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
throw new NoArgumentsException();
|
||||
}
|
||||
String commandName = args[0];
|
||||
String[] commandArguments = Arrays.copyOfRange(args, 1, args.length);
|
||||
find(commandName).run(commandArguments);
|
||||
}
|
||||
|
||||
private Command find(String name) {
|
||||
for (Command candidate : this.commands) {
|
||||
if (candidate.getName().equals(name)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
throw new NoSuchCommandException(name);
|
||||
}
|
||||
|
||||
protected void showUsage() {
|
||||
Log.info("usage: " + CLI_APP + " ");
|
||||
Log.info("");
|
||||
Log.info(" <command> [<args>]");
|
||||
Log.info("");
|
||||
Log.info("Available commands are:");
|
||||
for (Command command : this.commands) {
|
||||
String usageHelp = command.getUsageHelp();
|
||||
String description = command.getDescription();
|
||||
Log.info(String.format("\n %1$s %2$-15s\n %3$s", command.getName(),
|
||||
(usageHelp == null ? "" : usageHelp), (description == null ? ""
|
||||
: description)));
|
||||
}
|
||||
Log.info("");
|
||||
Log.info("See '" + CLI_APP
|
||||
+ " help <command>' for more information on a specific command.");
|
||||
}
|
||||
|
||||
protected void errorMessage(String message) {
|
||||
Log.error(message == null ? "Unexpected error" : message);
|
||||
}
|
||||
|
||||
protected void printStackTrace(Exception ex) {
|
||||
Log.error("");
|
||||
Log.error(ex);
|
||||
Log.error("");
|
||||
}
|
||||
|
||||
private String[] removeDebugFlags(String[] args) {
|
||||
List<String> rtn = new ArrayList<String>(args.length);
|
||||
for (String arg : args) {
|
||||
if (!("-d".equals(arg) || "--debug".equals(arg))) {
|
||||
rtn.add(arg);
|
||||
}
|
||||
}
|
||||
return rtn.toArray(new String[rtn.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal {@link Command} used for 'help' requests.
|
||||
*/
|
||||
private class HelpCommand implements Command {
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
throw new NoHelpCommandArgumentsException();
|
||||
}
|
||||
String commandName = args[0];
|
||||
for (Command command : SpringCli.this.commands) {
|
||||
if (command.getName().equals(commandName)) {
|
||||
Log.info(CLI_APP + " " + command.getName() + " - "
|
||||
+ command.getDescription());
|
||||
Log.info("");
|
||||
if (command.getUsageHelp() != null) {
|
||||
Log.info("usage: " + CLI_APP + " " + command.getName() + " "
|
||||
+ command.getUsageHelp());
|
||||
Log.info("");
|
||||
}
|
||||
if (command.getHelp() != null) {
|
||||
Log.info(command.getHelp());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new NoSuchCommandException(commandName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "help";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Get help on commands";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageHelp() {
|
||||
return "command";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelp() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NoHelpCommandArgumentsException extends SpringCliException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public NoHelpCommandArgumentsException() {
|
||||
super(Option.SHOW_USAGE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NoArgumentsException extends SpringCliException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The main CLI entry-point.
|
||||
* @param args CLI arguments
|
||||
*/
|
||||
public static void main(String... args) {
|
||||
int exitCode = new SpringCli().runAndHandleErrors(args);
|
||||
if (exitCode != 0) {
|
||||
System.exit(exitCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Runtime exception wrapper that defines additional {@link Option}s that are understood
|
||||
* by the {@link SpringCli}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class SpringCliException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
private final EnumSet<Option> options;
|
||||
|
||||
/**
|
||||
* Create a new {@link SpringCliException} with the specified options.
|
||||
* @param options the exception options
|
||||
*/
|
||||
public SpringCliException(Option... options) {
|
||||
this.options = asEnumSet(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SpringCliException} with the specified options.
|
||||
* @param message the exception message to display to the user
|
||||
* @param options the exception options
|
||||
*/
|
||||
public SpringCliException(String message, Option... options) {
|
||||
super(message);
|
||||
this.options = asEnumSet(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SpringCliException} with the specified options.
|
||||
* @param message the exception message to display to the user
|
||||
* @param cause the underlying cause
|
||||
* @param options the exception options
|
||||
*/
|
||||
public SpringCliException(String message, Throwable cause, Option... options) {
|
||||
super(message, cause);
|
||||
this.options = asEnumSet(options);
|
||||
}
|
||||
|
||||
private EnumSet<Option> asEnumSet(Option[] options) {
|
||||
if (options == null || options.length == 0) {
|
||||
return EnumSet.noneOf(Option.class);
|
||||
}
|
||||
return EnumSet.copyOf(Arrays.asList(options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns options a set of options that are understood by the {@link SpringCli}.
|
||||
*/
|
||||
public Set<Option> getOptions() {
|
||||
return Collections.unmodifiableSet(this.options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specific options understood by the {@link SpringCli}.
|
||||
*/
|
||||
public static enum Option {
|
||||
|
||||
/**
|
||||
* Print basic CLI usage information.
|
||||
*/
|
||||
SHOW_USAGE,
|
||||
|
||||
/**
|
||||
* Print the stack-trace of the exception.
|
||||
*/
|
||||
STACK_TRACE
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.boot.cli.command;
|
||||
|
||||
import org.springframework.boot.cli.Command;
|
||||
|
||||
/**
|
||||
* Abstract {@link Command} implementation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public abstract class AbstractCommand implements Command {
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* Create a new {@link AbstractCommand} instance.
|
||||
* @param name the name of the command
|
||||
* @param description the command description
|
||||
*/
|
||||
public AbstractCommand(String name, String description) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageHelp() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelp() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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 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.boot.cli.Command;
|
||||
import org.springframework.boot.cli.Log;
|
||||
|
||||
/**
|
||||
* {@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 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(), Layout.IVY);
|
||||
}
|
||||
if (options.has(this.mvnOption)) {
|
||||
if (options.has(this.ivyOption)) {
|
||||
clean(options, getGrapesHome(), Layout.IVY);
|
||||
}
|
||||
clean(options, getMavenHome(), Layout.MAVEN);
|
||||
}
|
||||
}
|
||||
|
||||
private void clean(OptionSet options, File root, Layout layout) {
|
||||
if (root == null || !root.exists()) {
|
||||
return;
|
||||
}
|
||||
ArrayList<Object> specs = new ArrayList<Object>(options.nonOptionArguments());
|
||||
if (!specs.contains("org.springframework.boot") && layout == Layout.IVY) {
|
||||
specs.add(0, "org.springframework.boot");
|
||||
}
|
||||
for (Object spec : specs) {
|
||||
if (spec instanceof String) {
|
||||
clean(options, root, layout, (String) spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void clean(OptionSet options, File root, Layout layout, String spec) {
|
||||
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()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.has(this.allOption) || group.equals("org.springframework.boot")) {
|
||||
delete(file);
|
||||
return;
|
||||
}
|
||||
|
||||
for (Object obj : FileUtil.listAll(file, Collections.emptyList())) {
|
||||
File candidate = (File) obj;
|
||||
if (candidate.getName().contains("SNAPSHOT")) {
|
||||
delete(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void delete(File file) {
|
||||
Log.info("Deleting: " + file);
|
||||
FileUtil.forceDelete(file);
|
||||
}
|
||||
|
||||
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() {
|
||||
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;
|
||||
}
|
||||
return new File(home, "grapes");
|
||||
}
|
||||
|
||||
private File getMavenHome() {
|
||||
String dir = System.getProperty("user.home");
|
||||
if (dir == null || !new File(dir).exists()) {
|
||||
return null;
|
||||
}
|
||||
File home = new File(dir);
|
||||
return new File(new File(home, ".m2"), "repository");
|
||||
}
|
||||
|
||||
private static enum Layout {
|
||||
IVY, MAVEN;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 joptsimple.OptionSet;
|
||||
|
||||
import org.springframework.boot.cli.Command;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* {@link Command} to 'create' a new spring groovy script.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class CreateCommand extends OptionParsingCommand {
|
||||
|
||||
public CreateCommand() {
|
||||
super("create", "Create an new spring groovy script", new CreateOptionHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageHelp() {
|
||||
return "[options] <file>";
|
||||
}
|
||||
|
||||
private static class CreateOptionHandler extends OptionHandler {
|
||||
|
||||
@Override
|
||||
protected void options() {
|
||||
option(asList("overwite", "f"), "Overwrite any existing file");
|
||||
option("type", "Create a specific application type").withOptionalArg()
|
||||
.ofType(String.class).describedAs("web, batch, integration");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(OptionSet options) {
|
||||
throw new IllegalStateException("Not implemented"); // FIXME
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.cli.Command;
|
||||
import org.springframework.boot.cli.CommandFactory;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link CommandFactory}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class DefaultCommandFactory implements CommandFactory {
|
||||
|
||||
private static final List<Command> DEFAULT_COMMANDS = Arrays.<Command> asList(
|
||||
new VersionCommand(), new RunCommand(), new CleanCommand());
|
||||
|
||||
@Override
|
||||
public Collection<Command> getCommands() {
|
||||
return DEFAULT_COMMANDS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 groovy.lang.Closure;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collection;
|
||||
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
import joptsimple.OptionSpecBuilder;
|
||||
|
||||
/**
|
||||
* A handler that parses and handles options.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see OptionParsingCommand
|
||||
*/
|
||||
public class OptionHandler {
|
||||
|
||||
private OptionParser parser;
|
||||
|
||||
private Closure<Void> closure;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public OptionParser getParser() {
|
||||
if (this.parser == null) {
|
||||
this.parser = new OptionParser();
|
||||
options();
|
||||
}
|
||||
return this.parser;
|
||||
}
|
||||
|
||||
protected void options() {
|
||||
if (this.closure != null) {
|
||||
this.closure.call();
|
||||
}
|
||||
}
|
||||
|
||||
public void setOptions(Closure<Void> closure) {
|
||||
this.closure = closure;
|
||||
}
|
||||
|
||||
public final void run(String... args) throws Exception {
|
||||
OptionSet options = getParser().parse(args);
|
||||
run(options);
|
||||
}
|
||||
|
||||
protected void run(OptionSet options) throws Exception {
|
||||
}
|
||||
|
||||
public String getHelp() {
|
||||
OutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
getParser().printHelpOn(out);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return "Help not available";
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.boot.cli.command;
|
||||
|
||||
import org.springframework.boot.cli.Command;
|
||||
|
||||
/**
|
||||
* Base class for a {@link Command} that pare options using an {@link OptionHandler}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public abstract class OptionParsingCommand extends AbstractCommand {
|
||||
|
||||
private OptionHandler handler;
|
||||
|
||||
public OptionParsingCommand(String name, String description, OptionHandler handler) {
|
||||
super(name, description);
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelp() {
|
||||
return this.handler.getHelp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void run(String... args) throws Exception {
|
||||
this.handler.run(args);
|
||||
}
|
||||
|
||||
protected OptionHandler getHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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 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.boot.cli.Command;
|
||||
import org.springframework.boot.cli.runner.SpringApplicationRunner;
|
||||
import org.springframework.boot.cli.runner.SpringApplicationRunnerConfiguration;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* {@link Command} to 'run' a groovy script or scripts.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @see SpringApplicationRunner
|
||||
*/
|
||||
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 OptionSpec<String> classpathOption;
|
||||
|
||||
private SpringApplicationRunner 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");
|
||||
this.classpathOption = option(asList("classpath", "cp"),
|
||||
"Additional classpath entries").withRequiredArg();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(OptionSet options) throws Exception {
|
||||
List<?> nonOptionArguments = options.nonOptionArguments();
|
||||
File[] files = getFileArguments(nonOptionArguments);
|
||||
List<?> args = nonOptionArguments.subList(files.length,
|
||||
nonOptionArguments.size());
|
||||
|
||||
if (options.has(this.editOption)) {
|
||||
Desktop.getDesktop().edit(files[0]);
|
||||
}
|
||||
|
||||
SpringApplicationRunnerConfiguration configuration = new SpringApplicationRunnerConfigurationAdapter(
|
||||
options);
|
||||
if (configuration.isLocal() && System.getProperty("grape.root") == null) {
|
||||
System.setProperty("grape.root", ".");
|
||||
}
|
||||
this.runner = new SpringApplicationRunner(configuration, files,
|
||||
args.toArray(new String[args.size()]));
|
||||
this.runner.compileAndRun();
|
||||
}
|
||||
|
||||
private File[] getFileArguments(List<?> nonOptionArguments) {
|
||||
List<File> files = new ArrayList<File>();
|
||||
for (Object option : nonOptionArguments) {
|
||||
if (option instanceof String) {
|
||||
String filename = (String) option;
|
||||
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 SpringApplicationRunnerConfiguration}.
|
||||
*/
|
||||
private class SpringApplicationRunnerConfigurationAdapter implements
|
||||
SpringApplicationRunnerConfiguration {
|
||||
|
||||
private OptionSet options;
|
||||
|
||||
public SpringApplicationRunnerConfigurationAdapter(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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClasspath() {
|
||||
if (this.options.has(RunOptionHandler.this.classpathOption)) {
|
||||
return this.options.valueOf(RunOptionHandler.this.classpathOption);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* 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 groovy.lang.Closure;
|
||||
import groovy.lang.GroovyObjectSupport;
|
||||
import groovy.lang.MetaClass;
|
||||
import groovy.lang.MetaMethod;
|
||||
import groovy.lang.Script;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import joptsimple.OptionParser;
|
||||
|
||||
import org.apache.ivy.util.FileUtil;
|
||||
import org.codehaus.groovy.control.CompilationFailedException;
|
||||
import org.springframework.boot.cli.Command;
|
||||
import org.springframework.boot.cli.compiler.GroovyCompiler;
|
||||
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration;
|
||||
|
||||
/**
|
||||
* {@link Command} to run a Groovy script.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
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;
|
||||
|
||||
public ScriptCommand(String script) {
|
||||
this.name = script;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
if (getMain() instanceof Command) {
|
||||
return ((Command) getMain()).getName();
|
||||
}
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
if (getMain() instanceof Command) {
|
||||
return ((Command) getMain()).getDescription();
|
||||
}
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelp() {
|
||||
if (getMain() instanceof OptionHandler) {
|
||||
return ((OptionHandler) getMain()).getHelp();
|
||||
}
|
||||
if (getMain() instanceof Command) {
|
||||
return ((Command) getMain()).getHelp();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
else if (main instanceof OptionHandler) {
|
||||
((OptionHandler) getMain()).run(args);
|
||||
}
|
||||
else if (main instanceof Closure) {
|
||||
((Closure<?>) 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();
|
||||
}
|
||||
return "[options] <args>";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private void compile() {
|
||||
GroovyCompiler compiler = new GroovyCompiler(new ScriptConfiguration());
|
||||
compiler.addCompilationCustomizers(new ScriptCompilationCustomizer());
|
||||
File source = locateSource(this.name);
|
||||
Class<?>[] classes;
|
||||
try {
|
||||
classes = compiler.compile(source);
|
||||
}
|
||||
catch (CompilationFailedException ex) {
|
||||
throw new IllegalStateException("Could not compile script", ex);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Could not compile script", ex);
|
||||
}
|
||||
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();
|
||||
FileUtil.copy(url, file, null);
|
||||
return file;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Could not create temp file for source: "
|
||||
+ name);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ScriptConfiguration implements GroovyCompilerConfiguration {
|
||||
|
||||
@Override
|
||||
public boolean isGuessImports() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGuessDependencies() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClasspath() {
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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 groovy.lang.Mixin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
import joptsimple.OptionSpecBuilder;
|
||||
|
||||
import org.codehaus.groovy.ast.AnnotationNode;
|
||||
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.expr.ArgumentListExpression;
|
||||
import org.codehaus.groovy.ast.expr.ClassExpression;
|
||||
import org.codehaus.groovy.ast.expr.ClosureExpression;
|
||||
import org.codehaus.groovy.ast.expr.Expression;
|
||||
import org.codehaus.groovy.ast.expr.MethodCallExpression;
|
||||
import org.codehaus.groovy.ast.stmt.BlockStatement;
|
||||
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
|
||||
import org.codehaus.groovy.ast.stmt.Statement;
|
||||
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;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.springframework.boot.cli.Command;
|
||||
|
||||
/**
|
||||
* Customizer for the compilation of CLI commands.
|
||||
*
|
||||
* @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 {
|
||||
addOptionHandlerMixin(classNode);
|
||||
overrideOptionsMethod(source, classNode);
|
||||
addImports(source, context, classNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add imports to the class node to make writing simple commands easier. No need to
|
||||
* import {@link OptionParser}, {@link OptionSet}, {@link Command} or
|
||||
* {@link OptionHandler}.
|
||||
*
|
||||
* @param source the source node
|
||||
* @param context the current context
|
||||
* @param classNode the class node to manipulate
|
||||
*/
|
||||
private void addImports(SourceUnit source, GeneratorContext context,
|
||||
ClassNode classNode) {
|
||||
ImportCustomizer importCustomizer = new ImportCustomizer();
|
||||
importCustomizer.addImports("joptsimple.OptionParser", "joptsimple.OptionSet",
|
||||
OptionParsingCommand.class.getCanonicalName(),
|
||||
Command.class.getCanonicalName(), OptionHandler.class.getCanonicalName());
|
||||
importCustomizer.call(source, context, classNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the script defines a block in this form:
|
||||
*
|
||||
* <pre>
|
||||
* options {
|
||||
* option "foo", "My Foo option"
|
||||
* option "bar", "Bar has a value" withOptionalArg() ofType Integer
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Then the block is taken and used to override the {@link OptionHandler#options()}
|
||||
* method. In the example "option" is a call to
|
||||
* {@link OptionHandler#option(String, String)}, and hence returns an
|
||||
* {@link OptionSpecBuilder}. Makes a nice readable DSL for adding options.
|
||||
*
|
||||
* @param source the source node
|
||||
* @param classNode the class node to manipulate
|
||||
*/
|
||||
private void overrideOptionsMethod(SourceUnit source, ClassNode classNode) {
|
||||
|
||||
BlockStatement block = source.getAST().getStatementBlock();
|
||||
List<Statement> statements = block.getStatements();
|
||||
|
||||
for (Statement statement : new ArrayList<Statement>(statements)) {
|
||||
if (statement instanceof ExpressionStatement) {
|
||||
ExpressionStatement expr = (ExpressionStatement) statement;
|
||||
Expression expression = expr.getExpression();
|
||||
if (expression instanceof MethodCallExpression) {
|
||||
MethodCallExpression method = (MethodCallExpression) expression;
|
||||
if (method.getMethod().getText().equals("options")) {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.springframework.boot.cli.Command;
|
||||
|
||||
/**
|
||||
* {@link Command} to display the 'version' number.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class VersionCommand extends AbstractCommand {
|
||||
|
||||
public VersionCommand() {
|
||||
super("--version", "Show the version");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
// FIXME: add version introspection
|
||||
throw new IllegalStateException("Not implemented");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.compiler;
|
||||
|
||||
import org.codehaus.groovy.ast.AnnotatedNode;
|
||||
import org.codehaus.groovy.ast.AnnotationNode;
|
||||
|
||||
/**
|
||||
* General purpose AST utilities.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public abstract class AstUtils {
|
||||
|
||||
/**
|
||||
* Determine if an {@link AnnotatedNode} has one or more of the specified annotations.
|
||||
*/
|
||||
public static boolean hasAtLeastOneAnnotation(AnnotatedNode node,
|
||||
String... annotations) {
|
||||
|
||||
for (AnnotationNode annotationNode : node.getAnnotations()) {
|
||||
for (String annotation : annotations) {
|
||||
if (annotation.equals(annotationNode.getClassNode().getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.compiler;
|
||||
|
||||
import groovy.lang.GroovyClassLoader;
|
||||
|
||||
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.ImportCustomizer;
|
||||
|
||||
/**
|
||||
* Strategy that can be used to apply some auto-configuration during the
|
||||
* {@link CompilePhase#CONVERSION} Groovy compile phase.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public abstract class CompilerAutoConfiguration {
|
||||
|
||||
/**
|
||||
* Strategy method used to determine when compiler auto-configuration should be
|
||||
* applied. Defaults to always.
|
||||
* @param classNode the class node
|
||||
* @return {@code true} if the compiler should be auto configured using this class. If
|
||||
* this method returns {@code false} no other strategy methods will be called.
|
||||
*/
|
||||
public boolean matches(ClassNode classNode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply any dependency customizations. This method will only be called if
|
||||
* {@link #matches} returns {@code true}.
|
||||
* @param dependencies dependency customizer
|
||||
* @throws CompilationFailedException
|
||||
*/
|
||||
public void applyDependencies(DependencyCustomizer dependencies)
|
||||
throws CompilationFailedException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply any import customizations. This method will only be called if
|
||||
* {@link #matches} returns {@code true}.
|
||||
* @param imports import customizer
|
||||
* @throws CompilationFailedException
|
||||
*/
|
||||
public void applyImports(ImportCustomizer imports) throws CompilationFailedException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply any customizations to the main class. This method will only be called if
|
||||
* {@link #matches} returns {@code true}. This method is useful when a groovy file
|
||||
* defines more than one class but customization only applies to the first class.
|
||||
*/
|
||||
public void applyToMainClass(GroovyClassLoader loader,
|
||||
GroovyCompilerConfiguration configuration, GeneratorContext generatorContext,
|
||||
SourceUnit source, ClassNode classNode) throws CompilationFailedException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply any additional configuration.
|
||||
*/
|
||||
public void apply(GroovyClassLoader loader,
|
||||
GroovyCompilerConfiguration configuration, GeneratorContext generatorContext,
|
||||
SourceUnit source, ClassNode classNode) throws CompilationFailedException {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* 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.compiler;
|
||||
|
||||
import groovy.grape.Grape;
|
||||
import groovy.lang.Grapes;
|
||||
import groovy.lang.GroovyClassLoader;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Customizer that allows dependencies to be added during compilation. Delegates to Groovy
|
||||
* {@link Grapes} to actually resolve dependencies. This class provides a fluent API for
|
||||
* conditionally adding dependencies. For example:
|
||||
* {@code dependencies.ifMissing("com.corp.SomeClass").add(group, module, version)}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class DependencyCustomizer {
|
||||
|
||||
private final GroovyClassLoader loader;
|
||||
|
||||
private final List<Map<String, Object>> dependencies;
|
||||
|
||||
private Properties properties;
|
||||
|
||||
/**
|
||||
* Create a new {@link DependencyCustomizer} instance. The {@link #call()} method must
|
||||
* be used to actually resolve dependencies.
|
||||
* @param loader
|
||||
*/
|
||||
public DependencyCustomizer(GroovyClassLoader loader) {
|
||||
this.loader = loader;
|
||||
this.dependencies = new ArrayList<Map<String, Object>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new nested {@link DependencyCustomizer}.
|
||||
* @param parent
|
||||
*/
|
||||
protected DependencyCustomizer(DependencyCustomizer parent) {
|
||||
this.loader = parent.loader;
|
||||
this.dependencies = parent.dependencies;
|
||||
}
|
||||
|
||||
public String getProperty(String key) {
|
||||
return getProperty(key, "");
|
||||
}
|
||||
|
||||
public String getProperty(String key, String defaultValue) {
|
||||
if (this.properties == null) {
|
||||
this.properties = new Properties();
|
||||
try {
|
||||
for (URL url : Collections.list(this.loader
|
||||
.getResources("META-INF/springcli.properties"))) {
|
||||
this.properties.load(url.openStream());
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
// swallow and continue
|
||||
}
|
||||
}
|
||||
return this.properties.getProperty(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a nested {@link DependencyCustomizer} that only applies if any of the
|
||||
* specified class names are not on the class path.
|
||||
* @param classNames the class names to test
|
||||
* @return a nested {@link DependencyCustomizer}
|
||||
*/
|
||||
public DependencyCustomizer ifAnyMissingClasses(final String... classNames) {
|
||||
return new DependencyCustomizer(this) {
|
||||
@Override
|
||||
protected boolean canAdd() {
|
||||
for (String classname : classNames) {
|
||||
try {
|
||||
DependencyCustomizer.this.loader.loadClass(classname);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return DependencyCustomizer.this.canAdd();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a nested {@link DependencyCustomizer} that only applies if all of the
|
||||
* specified class names are not on the class path.
|
||||
* @param classNames the class names to test
|
||||
* @return a nested {@link DependencyCustomizer}
|
||||
*/
|
||||
public DependencyCustomizer ifAllMissingClasses(final String... classNames) {
|
||||
return new DependencyCustomizer(this) {
|
||||
@Override
|
||||
protected boolean canAdd() {
|
||||
for (String classname : classNames) {
|
||||
try {
|
||||
DependencyCustomizer.this.loader.loadClass(classname);
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// swallow exception and continue
|
||||
}
|
||||
}
|
||||
return DependencyCustomizer.this.canAdd();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a nested {@link DependencyCustomizer} that only applies if the specified
|
||||
* paths are on the class path.
|
||||
* @param paths the paths to test
|
||||
* @return a nested {@link DependencyCustomizer}
|
||||
*/
|
||||
public DependencyCustomizer ifAllResourcesPresent(final String... paths) {
|
||||
return new DependencyCustomizer(this) {
|
||||
@Override
|
||||
protected boolean canAdd() {
|
||||
for (String path : paths) {
|
||||
try {
|
||||
if (DependencyCustomizer.this.loader.getResource(path) == null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// swallow exception and continue
|
||||
}
|
||||
}
|
||||
return DependencyCustomizer.this.canAdd();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a nested {@link DependencyCustomizer} that only applies at least one of the
|
||||
* specified paths is on the class path.
|
||||
* @param paths the paths to test
|
||||
* @return a nested {@link DependencyCustomizer}
|
||||
*/
|
||||
public DependencyCustomizer ifAnyResourcesPresent(final String... paths) {
|
||||
return new DependencyCustomizer(this) {
|
||||
@Override
|
||||
protected boolean canAdd() {
|
||||
for (String path : paths) {
|
||||
try {
|
||||
if (DependencyCustomizer.this.loader.getResource(path) != null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// swallow exception and continue
|
||||
}
|
||||
}
|
||||
return DependencyCustomizer.this.canAdd();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a nested {@link DependencyCustomizer} that only applies the specified one
|
||||
* was not yet added.
|
||||
* @return a nested {@link DependencyCustomizer}
|
||||
*/
|
||||
public DependencyCustomizer ifNotAdded(final String group, final String module) {
|
||||
return new DependencyCustomizer(this) {
|
||||
@Override
|
||||
protected boolean canAdd() {
|
||||
if (DependencyCustomizer.this.contains(group, module)) {
|
||||
return false;
|
||||
}
|
||||
return DependencyCustomizer.this.canAdd();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param group the group ID
|
||||
* @param module the module ID
|
||||
* @return true if this module is already in the dependencies
|
||||
*/
|
||||
protected boolean contains(String group, String module) {
|
||||
for (Map<String, Object> dependency : this.dependencies) {
|
||||
if (group.equals(dependency.get("group"))
|
||||
&& module.equals(dependency.get("module"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single dependencies.
|
||||
* @param group the group ID
|
||||
* @param module the module ID
|
||||
* @param version the version
|
||||
* @return this {@link DependencyCustomizer} for continued use
|
||||
*/
|
||||
public DependencyCustomizer add(String group, String module, String version) {
|
||||
return this.add(group, module, version, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single dependencies.
|
||||
* @param group the group ID
|
||||
* @param module the module ID
|
||||
* @param version the version
|
||||
* @return this {@link DependencyCustomizer} for continued use
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public DependencyCustomizer add(String group, String module, String version,
|
||||
boolean transitive) {
|
||||
if (canAdd()) {
|
||||
Map<String, Object> dependency = new HashMap<String, Object>();
|
||||
dependency.put("group", group);
|
||||
dependency.put("module", module);
|
||||
dependency.put("version", version);
|
||||
dependency.put("transitive", transitive);
|
||||
return add(dependency);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a dependencies.
|
||||
* @param dependencies a map of the dependencies to add.
|
||||
* @return this {@link DependencyCustomizer} for continued use
|
||||
*/
|
||||
public DependencyCustomizer add(Map<String, Object>... dependencies) {
|
||||
this.dependencies.addAll(Arrays.asList(dependencies));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy called to test if dependencies can be added. Subclasses override as
|
||||
* required.
|
||||
*/
|
||||
protected boolean canAdd() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the dependencies.
|
||||
*/
|
||||
void call() {
|
||||
HashMap<String, Object> args = new HashMap<String, Object>();
|
||||
args.put("classLoader", this.loader);
|
||||
Grape.grab(args, this.dependencies.toArray(new Map[this.dependencies.size()]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.compiler;
|
||||
|
||||
import groovy.lang.GroovyClassLoader;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.codehaus.groovy.ast.ClassNode;
|
||||
import org.codehaus.groovy.control.CompilationUnit;
|
||||
import org.codehaus.groovy.control.CompilerConfiguration;
|
||||
import org.codehaus.groovy.control.SourceUnit;
|
||||
|
||||
/**
|
||||
* Extension of the {@link GroovyClassLoader} with support for obtaining '.class' files as
|
||||
* resources.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
class ExtendedGroovyClassLoader extends GroovyClassLoader {
|
||||
|
||||
private Map<String, byte[]> classResources = new HashMap<String, byte[]>();
|
||||
private CompilerConfiguration configuration;
|
||||
|
||||
public ExtendedGroovyClassLoader(ClassLoader loader, CompilerConfiguration config) {
|
||||
super(loader, config);
|
||||
this.configuration = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(String name) {
|
||||
InputStream resourceStream = super.getResourceAsStream(name);
|
||||
if (resourceStream == null) {
|
||||
byte[] bytes = this.classResources.get(name);
|
||||
resourceStream = bytes == null ? null : new ByteArrayInputStream(bytes);
|
||||
}
|
||||
return resourceStream;
|
||||
}
|
||||
|
||||
public CompilerConfiguration getConfiguration() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassCollector createCollector(CompilationUnit unit, SourceUnit su) {
|
||||
InnerLoader loader = AccessController
|
||||
.doPrivileged(new PrivilegedAction<InnerLoader>() {
|
||||
@Override
|
||||
public InnerLoader run() {
|
||||
return new InnerLoader(ExtendedGroovyClassLoader.this);
|
||||
}
|
||||
});
|
||||
return new ExtendedClassCollector(loader, unit, su);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner collector class used to track as classes are added.
|
||||
*/
|
||||
protected class ExtendedClassCollector extends ClassCollector {
|
||||
|
||||
protected ExtendedClassCollector(InnerLoader loader, CompilationUnit unit,
|
||||
SourceUnit su) {
|
||||
super(loader, unit, su);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> createClass(byte[] code, ClassNode classNode) {
|
||||
Class<?> createdClass = super.createClass(code, classNode);
|
||||
ExtendedGroovyClassLoader.this.classResources.put(classNode.getName()
|
||||
.replace(".", "/") + ".class", code);
|
||||
return createdClass;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* 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.compiler;
|
||||
|
||||
import groovy.grape.Grape;
|
||||
import groovy.lang.GroovyClassLoader;
|
||||
import groovy.lang.GroovyClassLoader.ClassCollector;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
import org.codehaus.groovy.ast.ClassNode;
|
||||
import org.codehaus.groovy.classgen.GeneratorContext;
|
||||
import org.codehaus.groovy.control.CompilationFailedException;
|
||||
import org.codehaus.groovy.control.CompilationUnit;
|
||||
import org.codehaus.groovy.control.CompilePhase;
|
||||
import org.codehaus.groovy.control.CompilerConfiguration;
|
||||
import org.codehaus.groovy.control.Phases;
|
||||
import org.codehaus.groovy.control.SourceUnit;
|
||||
import org.codehaus.groovy.control.customizers.CompilationCustomizer;
|
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer;
|
||||
|
||||
/**
|
||||
* Compiler for Groovy source files. Primarily a simple Facade for
|
||||
* {@link GroovyClassLoader#parseClass(File)} with the following additional features:
|
||||
* <ul>
|
||||
* <li>{@link CompilerAutoConfiguration} strategies will be read from
|
||||
* <code>META-INF/services/org.springframework.boot.cli.compiler.CompilerAutoConfiguration</code>
|
||||
* (per the standard java {@link ServiceLoader} contract) and applied during compilation</li>
|
||||
*
|
||||
* <li>Multiple classes can be returned if the Groovy source defines more than one Class</li>
|
||||
*
|
||||
* <li>Generated class files can also be loaded using
|
||||
* {@link ClassLoader#getResource(String)}</li>
|
||||
* <ul>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class GroovyCompiler {
|
||||
|
||||
private GroovyCompilerConfiguration configuration;
|
||||
|
||||
private ExtendedGroovyClassLoader loader;
|
||||
|
||||
/**
|
||||
* Create a new {@link GroovyCompiler} instance.
|
||||
* @param configuration the compiler configuration
|
||||
*/
|
||||
public GroovyCompiler(final GroovyCompilerConfiguration configuration) {
|
||||
this.configuration = configuration;
|
||||
CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
|
||||
this.loader = new ExtendedGroovyClassLoader(getClass().getClassLoader(),
|
||||
compilerConfiguration);
|
||||
if (configuration.getClasspath().length() > 0) {
|
||||
this.loader.addClasspath(configuration.getClasspath());
|
||||
}
|
||||
// FIXME: allow the extra resolvers to be switched on (off by default)
|
||||
addExtraResolvers();
|
||||
compilerConfiguration
|
||||
.addCompilationCustomizers(new CompilerAutoConfigureCustomizer());
|
||||
}
|
||||
|
||||
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<Object> others = new ArrayList<Object>();
|
||||
for (File file : files) {
|
||||
if (file.getName().endsWith(".groovy") || file.getName().endsWith(".java")) {
|
||||
compilables.add(file);
|
||||
}
|
||||
else {
|
||||
others.add(file);
|
||||
}
|
||||
}
|
||||
Class<?>[] compiled = compile(compilables.toArray(new File[compilables.size()]));
|
||||
others.addAll(0, Arrays.asList(compiled));
|
||||
return others.toArray(new Object[others.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the specified Groovy source files, applying any
|
||||
* {@link CompilerAutoConfiguration}s. All classes defined in the files will be
|
||||
* returned from this method.
|
||||
* @param file the file to compile
|
||||
* @return compiled classes
|
||||
* @throws CompilationFailedException
|
||||
* @throws IOException
|
||||
*/
|
||||
public Class<?>[] compile(File... file) throws CompilationFailedException,
|
||||
IOException {
|
||||
|
||||
this.loader.clearCache();
|
||||
List<Class<?>> classes = new ArrayList<Class<?>>();
|
||||
|
||||
CompilerConfiguration compilerConfiguration = this.loader.getConfiguration();
|
||||
|
||||
CompilationUnit compilationUnit = new CompilationUnit(compilerConfiguration,
|
||||
null, this.loader);
|
||||
SourceUnit sourceUnit = new SourceUnit(file[0], compilerConfiguration,
|
||||
this.loader, compilationUnit.getErrorCollector());
|
||||
ClassCollector collector = this.loader.createCollector(compilationUnit,
|
||||
sourceUnit);
|
||||
compilationUnit.setClassgenCallback(collector);
|
||||
|
||||
compilationUnit.addSources(file);
|
||||
compilationUnit.compile(Phases.CLASS_GENERATION);
|
||||
for (Object loadedClass : collector.getLoadedClasses()) {
|
||||
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()]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link CompilationCustomizer} to call {@link CompilerAutoConfiguration}s.
|
||||
*/
|
||||
private class CompilerAutoConfigureCustomizer extends CompilationCustomizer {
|
||||
|
||||
public CompilerAutoConfigureCustomizer() {
|
||||
super(CompilePhase.CONVERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void call(SourceUnit source, GeneratorContext context, ClassNode classNode)
|
||||
throws CompilationFailedException {
|
||||
|
||||
ImportCustomizer importCustomizer = new ImportCustomizer();
|
||||
|
||||
ServiceLoader<CompilerAutoConfiguration> customizers = ServiceLoader.load(
|
||||
CompilerAutoConfiguration.class,
|
||||
GroovyCompiler.class.getClassLoader());
|
||||
|
||||
// Early sweep to get dependencies
|
||||
DependencyCustomizer dependencyCustomizer = new DependencyCustomizer(
|
||||
GroovyCompiler.this.loader);
|
||||
for (CompilerAutoConfiguration autoConfiguration : customizers) {
|
||||
if (autoConfiguration.matches(classNode)) {
|
||||
if (GroovyCompiler.this.configuration.isGuessDependencies()) {
|
||||
autoConfiguration.applyDependencies(dependencyCustomizer);
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencyCustomizer.call();
|
||||
|
||||
// Additional auto configuration
|
||||
for (CompilerAutoConfiguration autoConfiguration : customizers) {
|
||||
if (autoConfiguration.matches(classNode)) {
|
||||
if (GroovyCompiler.this.configuration.isGuessImports()) {
|
||||
autoConfiguration.applyImports(importCustomizer);
|
||||
importCustomizer.call(source, context, classNode);
|
||||
}
|
||||
if (source.getAST().getClasses().size() > 0
|
||||
&& classNode.equals(source.getAST().getClasses().get(0))) {
|
||||
autoConfiguration.applyToMainClass(GroovyCompiler.this.loader,
|
||||
GroovyCompiler.this.configuration, context, source,
|
||||
classNode);
|
||||
}
|
||||
autoConfiguration
|
||||
.apply(GroovyCompiler.this.loader,
|
||||
GroovyCompiler.this.configuration, context, source,
|
||||
classNode);
|
||||
}
|
||||
}
|
||||
importCustomizer.call(source, context, classNode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addExtraResolvers() {
|
||||
Map<String, Object> resolver = new HashMap<String, Object>();
|
||||
resolver.put("name", "spring-milestone");
|
||||
resolver.put("root", "http://repo.springsource.org/milestone");
|
||||
Grape.addResolver(resolver);
|
||||
resolver.put("name", "spring-snapshot");
|
||||
resolver.put("root", "http://repo.springsource.org/snapshot");
|
||||
Grape.addResolver(resolver);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.compiler;
|
||||
|
||||
/**
|
||||
* Configuration for the {@link GroovyCompiler}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public interface GroovyCompilerConfiguration {
|
||||
|
||||
/**
|
||||
* Returns if import declarations should be guessed.
|
||||
*/
|
||||
boolean isGuessImports();
|
||||
|
||||
/**
|
||||
* Returns if jar dependencies should be guessed.
|
||||
*/
|
||||
boolean isGuessDependencies();
|
||||
|
||||
/**
|
||||
* @return a path for local resources (colon separated)
|
||||
*/
|
||||
String getClasspath();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.compiler.autoconfigure;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.codehaus.groovy.ast.ClassNode;
|
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer;
|
||||
import org.springframework.boot.cli.compiler.AstUtils;
|
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
|
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer;
|
||||
|
||||
/**
|
||||
* {@link CompilerAutoConfiguration} for the Recator.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class ReactorCompilerAutoConfiguration extends CompilerAutoConfiguration {
|
||||
|
||||
@Override
|
||||
public boolean matches(ClassNode classNode) {
|
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableReactor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyDependencies(DependencyCustomizer dependencies) {
|
||||
dependencies
|
||||
.ifAnyMissingClasses("org.reactor.Reactor")
|
||||
.add("org.projectreactor", "reactor-spring",
|
||||
dependencies.getProperty("reactor.version"), false)
|
||||
.add("org.projectreactor", "reactor-core",
|
||||
dependencies.getProperty("reactor.version"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyImports(ImportCustomizer imports) {
|
||||
imports.addImports("reactor.core.Reactor", "reactor.event.Event",
|
||||
"reactor.spring.context.annotation.On",
|
||||
"reactor.spring.context.annotation.Reply",
|
||||
EnableReactor.class.getCanonicalName());
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public static @interface EnableReactor {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.compiler.autoconfigure;
|
||||
|
||||
import org.codehaus.groovy.ast.ClassNode;
|
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer;
|
||||
import org.springframework.boot.cli.compiler.AstUtils;
|
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
|
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer;
|
||||
|
||||
/**
|
||||
* {@link CompilerAutoConfiguration} for Spring Batch.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class SpringBatchCompilerAutoConfiguration extends CompilerAutoConfiguration {
|
||||
|
||||
@Override
|
||||
public boolean matches(ClassNode classNode) {
|
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableBatchProcessing");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyDependencies(DependencyCustomizer dependencies) {
|
||||
dependencies.ifAnyMissingClasses("org.springframework.batch.core.Job").add(
|
||||
"org.springframework.batch", "spring-batch-core",
|
||||
dependencies.getProperty("spring.batch.version", "2.2.0.RELEASE"));
|
||||
dependencies.ifAnyMissingClasses("org.springframework.jdbc.core.JdbcTemplate")
|
||||
.add("org.springframework", "spring-jdbc",
|
||||
dependencies.getProperty("spring.version"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyImports(ImportCustomizer imports) {
|
||||
imports.addImports(
|
||||
"org.springframework.batch.repeat.RepeatStatus",
|
||||
"org.springframework.batch.core.scope.context.ChunkContext",
|
||||
"org.springframework.batch.core.step.tasklet.Tasklet",
|
||||
"org.springframework.batch.core.configuration.annotation.StepScope",
|
||||
"org.springframework.batch.core.configuration.annotation.JobBuilderFactory",
|
||||
"org.springframework.batch.core.configuration.annotation.StepBuilderFactory",
|
||||
"org.springframework.batch.core.configuration.annotation.EnableBatchProcessing",
|
||||
"org.springframework.batch.core.Step",
|
||||
"org.springframework.batch.core.StepExecution",
|
||||
"org.springframework.batch.core.StepContribution",
|
||||
"org.springframework.batch.core.Job",
|
||||
"org.springframework.batch.core.JobExecution",
|
||||
"org.springframework.batch.core.JobParameter",
|
||||
"org.springframework.batch.core.JobParameters",
|
||||
"org.springframework.batch.core.launch.JobLauncher",
|
||||
"org.springframework.batch.core.converter.JobParametersConverter",
|
||||
"org.springframework.batch.core.converter.DefaultJobParametersConverter");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.compiler.autoconfigure;
|
||||
|
||||
import groovy.lang.GroovyClassLoader;
|
||||
|
||||
import org.codehaus.groovy.ast.AnnotationNode;
|
||||
import org.codehaus.groovy.ast.ClassNode;
|
||||
import org.codehaus.groovy.classgen.GeneratorContext;
|
||||
import org.codehaus.groovy.control.CompilationFailedException;
|
||||
import org.codehaus.groovy.control.SourceUnit;
|
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer;
|
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
|
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer;
|
||||
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration;
|
||||
|
||||
/**
|
||||
* {@link CompilerAutoConfiguration} for Spring.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class SpringBootCompilerAutoConfiguration extends CompilerAutoConfiguration {
|
||||
|
||||
@Override
|
||||
public void applyDependencies(DependencyCustomizer dependencies) {
|
||||
dependencies.ifAnyMissingClasses(
|
||||
"org.springframework.boot.strap.SpringApplication").add(
|
||||
"org.springframework.boot", "spring-boot-up",
|
||||
dependencies.getProperty("spring.boot.version"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyImports(ImportCustomizer imports) {
|
||||
imports.addImports("javax.sql.DataSource", "javax.annotation.PostConstruct",
|
||||
"javax.annotation.PreDestroy", "groovy.util.logging.Log",
|
||||
"org.springframework.stereotype.Controller",
|
||||
"org.springframework.stereotype.Service",
|
||||
"org.springframework.stereotype.Component",
|
||||
"org.springframework.beans.factory.annotation.Autowired",
|
||||
"org.springframework.beans.factory.annotation.Value",
|
||||
"org.springframework.context.annotation.Import",
|
||||
"org.springframework.context.annotation.ImportResource",
|
||||
"org.springframework.context.annotation.Profile",
|
||||
"org.springframework.context.annotation.Scope",
|
||||
"org.springframework.context.annotation.Configuration",
|
||||
"org.springframework.context.annotation.ComponentScan",
|
||||
"org.springframework.context.annotation.Bean",
|
||||
"org.springframework.context.ApplicationContext",
|
||||
"org.springframework.context.MessageSource",
|
||||
"org.springframework.core.annotation.Order",
|
||||
"org.springframework.core.io.ResourceLoader",
|
||||
"org.springframework.boot.strap.CommandLineRunner",
|
||||
"org.springframework.boot.config.EnableAutoConfiguration");
|
||||
imports.addStarImports("org.springframework.stereotype");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyToMainClass(GroovyClassLoader loader,
|
||||
GroovyCompilerConfiguration configuration, GeneratorContext generatorContext,
|
||||
SourceUnit source, ClassNode classNode) throws CompilationFailedException {
|
||||
// Could add switch for auto config, but it seems like it wouldn't get used much
|
||||
addEnableAutoConfigurationAnnotation(source, classNode);
|
||||
}
|
||||
|
||||
private void addEnableAutoConfigurationAnnotation(SourceUnit source,
|
||||
ClassNode classNode) {
|
||||
if (!hasEnableAutoConfigureAnnotation(classNode)) {
|
||||
try {
|
||||
Class<?> annotationClass = source.getClassLoader().loadClass(
|
||||
"org.springframework.boot.config.EnableAutoConfiguration");
|
||||
AnnotationNode annotationNode = new AnnotationNode(new ClassNode(
|
||||
annotationClass));
|
||||
classNode.addAnnotation(annotationNode);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasEnableAutoConfigureAnnotation(ClassNode classNode) {
|
||||
for (AnnotationNode node : classNode.getAnnotations()) {
|
||||
if ("EnableAutoConfiguration".equals(node.getClassNode()
|
||||
.getNameWithoutPackage())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.compiler.autoconfigure;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.codehaus.groovy.ast.ClassNode;
|
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer;
|
||||
import org.springframework.boot.cli.compiler.AstUtils;
|
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
|
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer;
|
||||
|
||||
/**
|
||||
* {@link CompilerAutoConfiguration} for Spring Integration.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class SpringIntegrationCompilerAutoConfiguration extends CompilerAutoConfiguration {
|
||||
|
||||
@Override
|
||||
public boolean matches(ClassNode classNode) {
|
||||
// Slightly weird detection algorithm because there is no @Enable annotation for
|
||||
// Integration
|
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "MessageEndpoint",
|
||||
"EnableIntegrationPatterns");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyDependencies(DependencyCustomizer dependencies) {
|
||||
dependencies
|
||||
.ifAnyMissingClasses("org.springframework.integration.Message")
|
||||
.add("org.springframework.integration", "spring-integration-core",
|
||||
dependencies.getProperty("spring.integration.version"))
|
||||
.add("org.springframework.integration",
|
||||
"spring-integration-dsl-groovy-core",
|
||||
dependencies.getProperty("spring.integration.dsl.version",
|
||||
"1.0.0.M1"));
|
||||
dependencies.ifAnyMissingClasses("groovy.util.XmlParser").add(
|
||||
"org.codehaus.groovy", "groovy-xml",
|
||||
dependencies.getProperty("groovy.version"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyImports(ImportCustomizer imports) {
|
||||
imports.addImports("org.springframework.integration.Message",
|
||||
"org.springframework.integration.support.MessageBuilder",
|
||||
"org.springframework.integration.MessageChannel",
|
||||
"org.springframework.integration.MessageHeaders",
|
||||
"org.springframework.integration.annotation.MessageEndpoint",
|
||||
"org.springframework.integration.annotation.Header",
|
||||
"org.springframework.integration.annotation.Headers",
|
||||
"org.springframework.integration.annotation.Payload",
|
||||
"org.springframework.integration.annotation.Payloads",
|
||||
EnableIntegrationPatterns.class.getCanonicalName(),
|
||||
"org.springframework.integration.dsl.groovy.MessageFlow",
|
||||
"org.springframework.integration.dsl.groovy.builder.IntegrationBuilder");
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public static @interface EnableIntegrationPatterns {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.compiler.autoconfigure;
|
||||
|
||||
import org.codehaus.groovy.ast.ClassNode;
|
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer;
|
||||
import org.springframework.boot.cli.compiler.AstUtils;
|
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
|
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer;
|
||||
|
||||
/**
|
||||
* {@link CompilerAutoConfiguration} for Spring MVC.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class SpringMvcCompilerAutoConfiguration extends CompilerAutoConfiguration {
|
||||
|
||||
@Override
|
||||
public void applyDependencies(DependencyCustomizer dependencies) {
|
||||
dependencies
|
||||
.ifAnyMissingClasses("org.springframework.web.servlet.mvc.Controller")
|
||||
.add("org.springframework", "spring-webmvc",
|
||||
dependencies.getProperty("spring.version"));
|
||||
|
||||
dependencies.ifAnyMissingClasses("org.eclipse.jetty.server.Server").add(
|
||||
"org.eclipse.jetty", "jetty-webapp",
|
||||
dependencies.getProperty("jetty.version"));
|
||||
|
||||
dependencies.add("org.codehaus.groovy", "groovy-templates",
|
||||
dependencies.getProperty("groovy.version"));
|
||||
// FIXME restore Tomcat when we can get reload to work
|
||||
// dependencies.ifMissingClasses("org.apache.catalina.startup.Tomcat")
|
||||
// .add("org.apache.tomcat.embed", "tomcat-embed-core",
|
||||
// dependencies.getProperty("tomcat.version"))
|
||||
// .add("org.apache.tomcat.embed", "tomcat-embed-logging-juli",
|
||||
// dependencies.getProperty("tomcat.version"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(ClassNode classNode) {
|
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "Controller", "EnableWebMvc");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyImports(ImportCustomizer imports) {
|
||||
imports.addStarImports("org.springframework.web.bind.annotation",
|
||||
"org.springframework.web.servlet.config.annotation",
|
||||
"org.springframework.web.servlet",
|
||||
"org.springframework.web.servlet.handler", "org.springframework.http");
|
||||
imports.addStaticImport("org.springframework.boot.cli.template.GroovyTemplate",
|
||||
"template");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.compiler.autoconfigure;
|
||||
|
||||
import org.codehaus.groovy.ast.ClassNode;
|
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer;
|
||||
import org.springframework.boot.cli.compiler.AstUtils;
|
||||
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
|
||||
import org.springframework.boot.cli.compiler.DependencyCustomizer;
|
||||
|
||||
/**
|
||||
* {@link CompilerAutoConfiguration} for Spring Security.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class SpringSecurityCompilerAutoConfiguration extends CompilerAutoConfiguration {
|
||||
|
||||
@Override
|
||||
public boolean matches(ClassNode classNode) {
|
||||
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableWebSecurity");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyDependencies(DependencyCustomizer dependencies) {
|
||||
dependencies
|
||||
.ifAnyMissingClasses(
|
||||
"org.springframework.security.config.annotation.web.configuration.EnableWebSecurity")
|
||||
.add("org.springframework.security", "spring-security-config",
|
||||
dependencies.getProperty("spring.security.version"))
|
||||
.add("org.springframework.security", "spring-security-web",
|
||||
dependencies.getProperty("spring.security.version"), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyImports(ImportCustomizer imports) {
|
||||
imports.addImports("org.springframework.security.core.Authentication",
|
||||
"org.springframework.security.core.authority.AuthorityUtils")
|
||||
.addStarImports(
|
||||
"org.springframework.security.config.annotation.web.configuration",
|
||||
"org.springframework.security.authentication",
|
||||
"org.springframework.security.config.annotation.web",
|
||||
"org.springframework.security.config.annotation.web.builders");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* 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.runner;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.springframework.boot.cli.compiler.GroovyCompiler;
|
||||
|
||||
/**
|
||||
* Compiles Groovy code running the resulting classes using a {@code SpringApplication}.
|
||||
* Takes care of threading and class-loading issues and can optionally monitor files for
|
||||
* changes.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class SpringApplicationRunner {
|
||||
|
||||
private static int watcherCounter = 0;
|
||||
|
||||
private static int runnerCounter = 0;
|
||||
|
||||
// FIXME logging
|
||||
|
||||
private SpringApplicationRunnerConfiguration configuration;
|
||||
|
||||
private final File[] files;
|
||||
|
||||
private final String[] args;
|
||||
|
||||
private final GroovyCompiler compiler;
|
||||
|
||||
private RunThread runThread;
|
||||
|
||||
private FileWatchThread fileWatchThread;
|
||||
|
||||
/**
|
||||
* Create a new {@link SpringApplicationRunner} instance.
|
||||
* @param configuration the configuration
|
||||
* @param files the files to compile/watch
|
||||
* @param args input arguments
|
||||
*/
|
||||
public SpringApplicationRunner(
|
||||
final SpringApplicationRunnerConfiguration configuration, File[] files,
|
||||
String... args) {
|
||||
this.configuration = configuration;
|
||||
this.files = files.clone();
|
||||
this.args = args.clone();
|
||||
this.compiler = new GroovyCompiler(configuration);
|
||||
if (configuration.getLogLevel().intValue() <= Level.FINE.intValue()) {
|
||||
System.setProperty("groovy.grape.report.downloads", "true");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile and run the application. This method is synchronized as it can be called by
|
||||
* file monitoring threads.
|
||||
* @throws Exception on error
|
||||
*/
|
||||
public synchronized void compileAndRun() throws Exception {
|
||||
try {
|
||||
|
||||
stop();
|
||||
|
||||
// Compile
|
||||
Object[] sources = this.compiler.sources(this.files);
|
||||
if (sources.length == 0) {
|
||||
throw new RuntimeException("No classes found in '" + this.files + "'");
|
||||
}
|
||||
|
||||
// Run in new thread to ensure that the context classloader is setup
|
||||
this.runThread = new RunThread(sources);
|
||||
this.runThread.start();
|
||||
this.runThread.join();
|
||||
|
||||
// Start monitoring for changes
|
||||
if (this.fileWatchThread == null
|
||||
&& this.configuration.isWatchForFileChanges()) {
|
||||
this.fileWatchThread = new FileWatchThread();
|
||||
this.fileWatchThread.start();
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (this.fileWatchThread == null) {
|
||||
throw ex;
|
||||
}
|
||||
else {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread used to launch the Spring Application with the correct context classloader.
|
||||
*/
|
||||
private class RunThread extends Thread {
|
||||
|
||||
private final Object[] sources;
|
||||
|
||||
private Object applicationContext;
|
||||
|
||||
/**
|
||||
* Create a new {@link RunThread} instance.
|
||||
* @param sources the sources to launch
|
||||
*/
|
||||
public RunThread(Object... sources) {
|
||||
super("runner-" + (runnerCounter++));
|
||||
this.sources = sources;
|
||||
if (sources.length != 0 && sources[0] instanceof Class) {
|
||||
setContextClassLoader(((Class<?>) sources[0]).getClassLoader());
|
||||
}
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// User reflection to load and call Spring
|
||||
Class<?> application = getContextClassLoader().loadClass(
|
||||
"org.springframework.boot.strap.SpringApplication");
|
||||
Method method = application.getMethod("run", Object[].class,
|
||||
String[].class);
|
||||
this.applicationContext = method.invoke(null, this.sources,
|
||||
SpringApplicationRunner.this.args);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the thread, closing any previously opened application context.
|
||||
*/
|
||||
public synchronized void shutdown() {
|
||||
if (this.applicationContext != null) {
|
||||
try {
|
||||
Method method = this.applicationContext.getClass().getMethod("close");
|
||||
method.invoke(this.applicationContext);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
// Not an application context that we can close
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
this.applicationContext = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread to watch for file changes and trigger recompile/reload.
|
||||
*/
|
||||
private class FileWatchThread extends Thread {
|
||||
|
||||
private long previous;
|
||||
|
||||
public FileWatchThread() {
|
||||
super("filewatcher-" + (watcherCounter++));
|
||||
this.previous = 0;
|
||||
for (File file : SpringApplicationRunner.this.files) {
|
||||
long current = file.lastModified();
|
||||
if (current > this.previous) {
|
||||
this.previous = current;
|
||||
}
|
||||
}
|
||||
setDaemon(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
|
||||
for (File file : SpringApplicationRunner.this.files) {
|
||||
long current = file.lastModified();
|
||||
if (this.previous < current) {
|
||||
this.previous = current;
|
||||
compileAndRun();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Swallow, will be reported by compileAndRun
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (this.runThread != null) {
|
||||
this.runThread.shutdown();
|
||||
this.runThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.runner;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration;
|
||||
|
||||
/**
|
||||
* Configuration for the {@link SpringApplicationRunner}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public interface SpringApplicationRunnerConfiguration extends GroovyCompilerConfiguration {
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the source file should be monitored for changes and
|
||||
* automatically recompiled.
|
||||
*/
|
||||
boolean isWatchForFileChanges();
|
||||
|
||||
/**
|
||||
* Returns the logging level to use.
|
||||
*/
|
||||
Level getLogLevel();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the dependencies should be cached locally
|
||||
*/
|
||||
boolean isLocal();
|
||||
|
||||
}
|
||||
@@ -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.boot.cli.template;
|
||||
|
||||
import groovy.text.GStringTemplateEngine;
|
||||
import groovy.text.Template;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.codehaus.groovy.control.CompilationFailedException;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public abstract class GroovyTemplate {
|
||||
|
||||
// FIXME is this used?
|
||||
|
||||
public static String template(String name) throws IOException,
|
||||
CompilationFailedException, ClassNotFoundException {
|
||||
return template(name, Collections.<String, Object> emptyMap());
|
||||
}
|
||||
|
||||
public static String template(String name, Map<String, ?> model) throws IOException,
|
||||
CompilationFailedException, ClassNotFoundException {
|
||||
GStringTemplateEngine engine = new GStringTemplateEngine();
|
||||
File file = new File("templates", name);
|
||||
URL resource = GroovyTemplate.class.getClassLoader().getResource(
|
||||
"templates/" + name);
|
||||
Template template;
|
||||
if (file.exists()) {
|
||||
template = engine.createTemplate(file);
|
||||
}
|
||||
else {
|
||||
if (resource != null) {
|
||||
template = engine.createTemplate(resource);
|
||||
}
|
||||
else {
|
||||
template = engine.createTemplate(name);
|
||||
}
|
||||
}
|
||||
return template.make(model).toString();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user