Fast forward existing prototype work
This commit is contained in:
30
spring-bootstrap-cli/src/main/assembly/descriptor.xml
Normal file
30
spring-bootstrap-cli/src/main/assembly/descriptor.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<assembly>
|
||||
<id>dist</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
<format>dir</format>
|
||||
</formats>
|
||||
<baseDirectory>spring-${project.version}</baseDirectory>
|
||||
<includeBaseDirectory>true</includeBaseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>src/main/scripts</directory>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>src/main/resources</directory>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
<filtered>true</filtered>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<includes>
|
||||
<include>org.springframework.bootstrap:spring-bootstrap-cli:jar:*</include>
|
||||
</includes>
|
||||
<outputDirectory>lib</outputDirectory>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
</assembly>
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
|
||||
/**
|
||||
* Abstract {@link Command} implementation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public abstract class AbstractCommand implements Command {
|
||||
|
||||
private String name;
|
||||
|
||||
private boolean optionCommand;
|
||||
|
||||
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, description, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AbstractCommand} instance.
|
||||
* @param name the name of the command
|
||||
* @param description the command description
|
||||
* @param optionCommand if this command is an option command (see
|
||||
* {@link Command#isOptionCommand()}
|
||||
*/
|
||||
public AbstractCommand(String name, String description, boolean optionCommand) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.optionCommand = optionCommand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOptionCommand() {
|
||||
return this.optionCommand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageHelp() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printHelp(PrintStream out) throws IOException {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli;
|
||||
|
||||
import java.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 SpringBootstrapCli}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class BootstrapCliException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
private final EnumSet<Option> options;
|
||||
|
||||
/**
|
||||
* Create a new {@link BootstrapCliException} with the specified options.
|
||||
* @param options the exception options
|
||||
*/
|
||||
public BootstrapCliException(Option... options) {
|
||||
this.options = asEnumSet(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link BootstrapCliException} with the specified options.
|
||||
* @param message the exception message to display to the user
|
||||
* @param options the exception options
|
||||
*/
|
||||
public BootstrapCliException(String message, Option... options) {
|
||||
super(message);
|
||||
this.options = asEnumSet(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link BootstrapCliException} 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 BootstrapCliException(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 SpringBootstrapCli}.
|
||||
*/
|
||||
public Set<Option> getOptions() {
|
||||
return Collections.unmodifiableSet(this.options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specific options understood by the {@link SpringBootstrapCli}.
|
||||
*/
|
||||
public static enum Option {
|
||||
|
||||
/**
|
||||
* Print basic CLI usage information.
|
||||
*/
|
||||
SHOW_USAGE,
|
||||
|
||||
/**
|
||||
* Print the stack-trace of the exception.
|
||||
*/
|
||||
STACK_TRACE
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
|
||||
/**
|
||||
* A single command that can be run from the CLI.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see #run(String...)
|
||||
*/
|
||||
public interface Command {
|
||||
|
||||
/**
|
||||
* Returns the name of the command.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this is an 'option command'. An option command is a special
|
||||
* type of command that usually makes more sense to present as if it is an option. For
|
||||
* example '--help'.
|
||||
*/
|
||||
boolean isOptionCommand();
|
||||
|
||||
/**
|
||||
* 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. eg. '[options] <file>'. Do not include the name of
|
||||
* the command in this string.
|
||||
*/
|
||||
String getUsageHelp();
|
||||
|
||||
/**
|
||||
* Prints help for the command.
|
||||
* @param out the output writer to display help
|
||||
* @throws IOException
|
||||
*/
|
||||
void printHelp(PrintStream out) throws IOException;
|
||||
|
||||
/**
|
||||
* 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,54 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli;
|
||||
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
|
||||
/**
|
||||
* {@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");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageHelp() {
|
||||
return "[options] <file>";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OptionParser createOptionParser() {
|
||||
OptionParser parser = new OptionParser();
|
||||
parser.acceptsAll(asList("overwite", "f"), "Overwrite any existing file");
|
||||
parser.accepts("type", "Create a specific application type").withOptionalArg()
|
||||
.ofType(String.class).describedAs("web, batch, integration");
|
||||
return parser;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(OptionSet options) {
|
||||
throw new IllegalStateException("Not implemented"); // FIXME
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli;
|
||||
|
||||
/**
|
||||
* Exception thrown when no CLI options are specified.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class NoArgumentsException extends BootstrapCliException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli;
|
||||
|
||||
/**
|
||||
* Exception thrown when the 'help' command is issued without any arguments.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class NoHelpCommandArgumentsException extends BootstrapCliException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public NoHelpCommandArgumentsException() {
|
||||
super(Option.SHOW_USAGE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.bootstrap.cli;
|
||||
|
||||
/**
|
||||
* Exception thrown when an unknown command is specified.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class NoSuchCommandException extends BootstrapCliException {
|
||||
|
||||
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'.",
|
||||
SpringBootstrapCli.CLI_APP, name));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli;
|
||||
|
||||
/**
|
||||
* Exception thrown when an unknown root option is specified. This only applies to
|
||||
* {@link Command#isOptionCommand() option command}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class NoSuchOptionException extends BootstrapCliException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public NoSuchOptionException(String name) {
|
||||
super("Unknown option: --" + name, Option.SHOW_USAGE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
|
||||
/**
|
||||
* Base class for any {@link Command}s that use an {@link OptionParser}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public abstract class OptionParsingCommand extends AbstractCommand {
|
||||
|
||||
private OptionParser parser;
|
||||
|
||||
public OptionParsingCommand(String name, String description) {
|
||||
super(name, description);
|
||||
this.parser = createOptionParser();
|
||||
}
|
||||
|
||||
protected abstract OptionParser createOptionParser();
|
||||
|
||||
@Override
|
||||
public void printHelp(PrintStream out) throws IOException {
|
||||
this.parser.printHelpOn(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void run(String... args) throws Exception {
|
||||
OptionSet options = parser.parse(args);
|
||||
run(options);
|
||||
}
|
||||
|
||||
protected abstract void run(OptionSet options) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli;
|
||||
|
||||
import java.awt.Desktop;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import org.springframework.bootstrap.cli.runner.BootstrapRunner;
|
||||
import org.springframework.bootstrap.cli.runner.BootstrapRunnerConfiguration;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* {@link Command} to 'run' a spring groovy script.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see BootstrapRunner
|
||||
*/
|
||||
public class RunCommand extends OptionParsingCommand {
|
||||
|
||||
private OptionSpec<Void> noWatchOption; // FIXME
|
||||
|
||||
private OptionSpec<Void> editOption;
|
||||
|
||||
private OptionSpec<Void> noGuessImportsOption;
|
||||
|
||||
private OptionSpec<Void> noGuessDependenciesOption;
|
||||
|
||||
private OptionSpec<Void> verboseOption;
|
||||
|
||||
private OptionSpec<Void> quiteOption;
|
||||
|
||||
public RunCommand() {
|
||||
super("run", "Run a spring groovy script");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageHelp() {
|
||||
return "[options] <file>";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OptionParser createOptionParser() {
|
||||
OptionParser parser = new OptionParser();
|
||||
this.noWatchOption = parser.accepts("no-watch",
|
||||
"Do not watch the specified file for changes");
|
||||
this.editOption = parser.acceptsAll(asList("edit", "e"),
|
||||
"Open the file with the default system editor");
|
||||
this.noGuessImportsOption = parser.accepts("no-guess-imports",
|
||||
"Do not attempt to guess imports");
|
||||
this.noGuessDependenciesOption = parser.accepts("no-guess-dependencies",
|
||||
"Do not attempt to guess dependencies");
|
||||
this.verboseOption = parser.acceptsAll(asList("verbose", "v"), "Verbose logging");
|
||||
this.quiteOption = parser.acceptsAll(asList("quiet", "q"), "Quiet logging");
|
||||
return parser;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(OptionSet options) throws Exception {
|
||||
List<String> nonOptionArguments = options.nonOptionArguments();
|
||||
File file = getFileArgument(nonOptionArguments);
|
||||
List<String> args = nonOptionArguments.subList(1, nonOptionArguments.size());
|
||||
|
||||
if (options.has(this.editOption)) {
|
||||
Desktop.getDesktop().edit(file);
|
||||
}
|
||||
|
||||
BootstrapRunnerConfiguration configuration = new BootstrapRunnerConfigurationAdapter(
|
||||
options);
|
||||
new BootstrapRunner(configuration, file, args.toArray(new String[args.size()]))
|
||||
.compileAndRun();
|
||||
}
|
||||
|
||||
private File getFileArgument(List<String> nonOptionArguments) {
|
||||
if (nonOptionArguments.size() == 0) {
|
||||
throw new RuntimeException("Please specify a file to run");
|
||||
}
|
||||
String filename = nonOptionArguments.get(0);
|
||||
File file = new File(filename);
|
||||
if (!file.isFile() || !file.canRead()) {
|
||||
throw new RuntimeException("Unable to read '" + filename + "'");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple adapter class to present the {@link OptionSet} as a
|
||||
* {@link BootstrapRunnerConfiguration}.
|
||||
*/
|
||||
private class BootstrapRunnerConfigurationAdapter implements
|
||||
BootstrapRunnerConfiguration {
|
||||
|
||||
private OptionSet options;
|
||||
|
||||
public BootstrapRunnerConfigurationAdapter(OptionSet options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWatchForFileChanges() {
|
||||
return !this.options.has(RunCommand.this.noWatchOption);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGuessImports() {
|
||||
return !this.options.has(RunCommand.this.noGuessImportsOption);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGuessDependencies() {
|
||||
return !this.options.has(RunCommand.this.noGuessDependenciesOption);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Level getLogLevel() {
|
||||
if (this.options.has(RunCommand.this.verboseOption)) {
|
||||
return Level.FINEST;
|
||||
}
|
||||
if (this.options.has(RunCommand.this.quiteOption)) {
|
||||
return Level.OFF;
|
||||
}
|
||||
return Level.INFO;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Spring Bootstrap Command Line Interface. This is the main entry-point for the spring
|
||||
* bootstrap 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. The {@link OptionParsingCommand}
|
||||
* class provides a convenient base for command that need to parse arguments.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see #main(String...)
|
||||
* @see BootstrapCliException
|
||||
* @see Command
|
||||
* @see OptionParsingCommand
|
||||
*/
|
||||
public class SpringBootstrapCli {
|
||||
|
||||
public static final String CLI_APP = "spr";
|
||||
|
||||
private static final Set<BootstrapCliException.Option> NO_EXCEPTION_OPTIONS = EnumSet
|
||||
.noneOf(BootstrapCliException.Option.class);
|
||||
|
||||
private List<Command> commands;
|
||||
|
||||
/**
|
||||
* Create a new {@link SpringBootstrapCli} implementation with the default set of
|
||||
* commands.
|
||||
*/
|
||||
public SpringBootstrapCli() {
|
||||
setCommands(Arrays.asList(new VersionCommand(), new RunCommand(),
|
||||
new CreateCommand()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
protected 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 zero 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) {
|
||||
Set<BootstrapCliException.Option> options = NO_EXCEPTION_OPTIONS;
|
||||
if (ex instanceof BootstrapCliException) {
|
||||
options = ((BootstrapCliException) ex).getOptions();
|
||||
}
|
||||
errorMessage(ex.getMessage());
|
||||
if (options.contains(BootstrapCliException.Option.SHOW_USAGE)) {
|
||||
showUsage();
|
||||
}
|
||||
if (debug || options.contains(BootstrapCliException.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) {
|
||||
boolean isOption = name.startsWith("--");
|
||||
if (isOption) {
|
||||
name = name.substring(2);
|
||||
}
|
||||
for (Command candidate : this.commands) {
|
||||
if ((isOption && candidate.isOptionCommand() || !isOption)
|
||||
&& candidate.getName().equals(name)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
throw (isOption ? new NoSuchOptionException(name) : new NoSuchCommandException(
|
||||
name));
|
||||
}
|
||||
|
||||
protected void showUsage() {
|
||||
System.out.print("usage: " + CLI_APP + " ");
|
||||
for (Command command : this.commands) {
|
||||
if (command.isOptionCommand()) {
|
||||
System.out.print("[--" + command.getName() + "] ");
|
||||
}
|
||||
}
|
||||
System.out.println("");
|
||||
System.out.println(" <command> [<args>]");
|
||||
System.out.println("");
|
||||
System.out.println("Available commands are:");
|
||||
for (Command command : this.commands) {
|
||||
if (!command.isOptionCommand()) {
|
||||
System.out.println(String.format(" %1$-15s %2$s", command.getName(),
|
||||
command.getDescription()));
|
||||
}
|
||||
}
|
||||
System.out.println("");
|
||||
System.out
|
||||
.println("See 'spr help <command>' for more information on a specific command.");
|
||||
}
|
||||
|
||||
protected void errorMessage(String message) {
|
||||
System.err.println(message == null ? "Unexpected error" : message);
|
||||
}
|
||||
|
||||
protected void printStackTrace(Exception ex) {
|
||||
System.err.println("");
|
||||
ex.printStackTrace(System.err);
|
||||
System.err.println("");
|
||||
}
|
||||
|
||||
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' and '--help' requests.
|
||||
*/
|
||||
private class HelpCommand extends AbstractCommand {
|
||||
|
||||
public HelpCommand() {
|
||||
super("help", "Show command help", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
throw new NoHelpCommandArgumentsException();
|
||||
}
|
||||
String commandName = args[0];
|
||||
for (Command command : SpringBootstrapCli.this.commands) {
|
||||
if (!command.isOptionCommand() && command.getName().equals(commandName)) {
|
||||
System.out.println(CLI_APP + " " + command.getName() + " - "
|
||||
+ command.getDescription());
|
||||
System.out.println();
|
||||
if (command.getUsageHelp() != null) {
|
||||
System.out.println("usage: " + CLI_APP + " " + command.getName()
|
||||
+ " " + command.getUsageHelp());
|
||||
System.out.println();
|
||||
}
|
||||
command.printHelp(System.out);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new NoSuchCommandException(commandName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The main CLI entry-point.
|
||||
* @param args CLI arguments
|
||||
*/
|
||||
public static void main(String... args) {
|
||||
int exitCode = new SpringBootstrapCli().runAndHandleErrors(args);
|
||||
if (exitCode != 0) {
|
||||
System.exit(exitCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli;
|
||||
|
||||
/**
|
||||
* {@link Command} to displat the 'version' number.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class VersionCommand extends AbstractCommand {
|
||||
|
||||
public VersionCommand() {
|
||||
super("version", "Show the version", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
throw new IllegalStateException("Not implemented"); // FIXME
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli.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 hasLeastOneAnnotation(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.bootstrap.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,130 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli.compiler;
|
||||
|
||||
import groovy.grape.Grape;
|
||||
import groovy.lang.Grapes;
|
||||
import groovy.lang.GroovyClassLoader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a nested {@link DependencyCustomizer} that only applies if the specified
|
||||
* class names are not on the class path.
|
||||
* @param classNames the class names to test
|
||||
* @return a nested {@link DependencyCustomizer}
|
||||
*/
|
||||
public DependencyCustomizer ifMissingClasses(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 e) {
|
||||
}
|
||||
}
|
||||
return DependencyCustomizer.this.canAdd();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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", true);
|
||||
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
|
||||
* requred.
|
||||
*/
|
||||
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,88 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli.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} that support for obtaining '.class' files as
|
||||
* resources.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ExtendedGroovyClassLoader extends GroovyClassLoader {
|
||||
|
||||
private Map<String, byte[]> classResources = new HashMap<String, byte[]>();
|
||||
|
||||
public ExtendedGroovyClassLoader(ClassLoader loader, CompilerConfiguration config) {
|
||||
super(loader, 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected 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,148 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli.compiler;
|
||||
|
||||
import groovy.lang.GroovyClassLoader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.CompilerConfiguration;
|
||||
import org.codehaus.groovy.control.SourceUnit;
|
||||
import org.codehaus.groovy.control.customizers.CompilationCustomizer;
|
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer;
|
||||
import org.springframework.bootstrap.cli.compiler.autoconfigure.SpringBootstrapCompilerAutoConfiguration;
|
||||
import org.springframework.bootstrap.cli.compiler.autoconfigure.SpringMvcCompilerAutoConfiguration;
|
||||
|
||||
/**
|
||||
* 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 de 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
|
||||
*/
|
||||
public class GroovyCompiler {
|
||||
|
||||
// FIXME could be a strategy
|
||||
private static final CompilerAutoConfiguration[] COMPILER_AUTO_CONFIGURATIONS = {
|
||||
new SpringBootstrapCompilerAutoConfiguration(),
|
||||
new SpringMvcCompilerAutoConfiguration(),
|
||||
new SpringBootstrapCompilerAutoConfiguration() };
|
||||
|
||||
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);
|
||||
compilerConfiguration
|
||||
.addCompilationCustomizers(new CompilerAutoConfigureCustomizer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the specified Groovy source files, applying any
|
||||
* {@link CompilerAutoConfiguration}s. All classes defined in the file will be
|
||||
* returned from this method with the first item being the primary class (defined at
|
||||
* the top of the file).
|
||||
* @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<?>>();
|
||||
Class<?> mainClass = this.loader.parseClass(file);
|
||||
for (Class<?> loadedClass : this.loader.getLoadedClasses()) {
|
||||
classes.add(loadedClass);
|
||||
}
|
||||
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();
|
||||
|
||||
// Early sweep to get dependencies
|
||||
DependencyCustomizer dependencyCustomizer = new DependencyCustomizer(
|
||||
GroovyCompiler.this.loader);
|
||||
for (CompilerAutoConfiguration autoConfiguration : COMPILER_AUTO_CONFIGURATIONS) {
|
||||
if (autoConfiguration.matches(classNode)) {
|
||||
if (GroovyCompiler.this.configuration.isGuessDependencies()) {
|
||||
autoConfiguration.applyDependencies(dependencyCustomizer);
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencyCustomizer.call();
|
||||
|
||||
// Additional auto configuration
|
||||
for (CompilerAutoConfiguration autoConfiguration : COMPILER_AUTO_CONFIGURATIONS) {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.bootstrap.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();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli.compiler.autoconfigure;
|
||||
|
||||
import org.codehaus.groovy.ast.ClassNode;
|
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer;
|
||||
import org.springframework.bootstrap.cli.compiler.AstUtils;
|
||||
import org.springframework.bootstrap.cli.compiler.CompilerAutoConfiguration;
|
||||
import org.springframework.bootstrap.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.hasLeastOneAnnotation(classNode, "EnableBatchProcessing");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyDependencies(DependencyCustomizer dependencies) {
|
||||
dependencies.ifMissingClasses("org.springframework.batch.core.Job").add(
|
||||
"org.springframework.batch", "spring-batch-core", "2.1.9.RELEASE");
|
||||
}
|
||||
|
||||
@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.DefaultJobParametersConverter");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli.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.bootstrap.cli.compiler.CompilerAutoConfiguration;
|
||||
import org.springframework.bootstrap.cli.compiler.DependencyCustomizer;
|
||||
import org.springframework.bootstrap.cli.compiler.GroovyCompilerConfiguration;
|
||||
|
||||
/**
|
||||
* {@link CompilerAutoConfiguration} for Spring Bootstrap.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class SpringBootstrapCompilerAutoConfiguration extends CompilerAutoConfiguration {
|
||||
|
||||
@Override
|
||||
public void applyDependencies(DependencyCustomizer dependencies) {
|
||||
dependencies.ifMissingClasses("org.springframework.bootstrap.SpringApplication")
|
||||
.add("org.springframework.bootstrap", "spring-bootstrap-application",
|
||||
"0.0.1-SNAPSHOT");
|
||||
// FIXME get the version
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyImports(ImportCustomizer imports) {
|
||||
imports.addImports("javax.sql.DataSource",
|
||||
"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.Bean",
|
||||
"org.springframework.bootstrap.context.annotation.EnableAutoConfiguration");
|
||||
imports.addStarImports("org.springframework.stereotype");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyToMainClass(GroovyClassLoader loader,
|
||||
GroovyCompilerConfiguration configuration, GeneratorContext generatorContext,
|
||||
SourceUnit source, ClassNode classNode) throws CompilationFailedException {
|
||||
if (true) {
|
||||
addEnableAutoConfigurationAnnotation(source, classNode);
|
||||
}
|
||||
}
|
||||
|
||||
private void addEnableAutoConfigurationAnnotation(SourceUnit source,
|
||||
ClassNode classNode) {
|
||||
if (!hasEnableAutoConfigureAnnotation(classNode)) {
|
||||
try {
|
||||
Class<?> annotationClass = source
|
||||
.getClassLoader()
|
||||
.loadClass(
|
||||
"org.springframework.bootstrap.context.annotation.EnableAutoConfiguration");
|
||||
AnnotationNode annotationNode = new AnnotationNode(new ClassNode(
|
||||
annotationClass));
|
||||
classNode.addAnnotation(annotationNode);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasEnableAutoConfigureAnnotation(ClassNode classNode) {
|
||||
for (AnnotationNode node : classNode.getAnnotations()) {
|
||||
if ("EnableAutoConfiguration".equals(node.getClassNode()
|
||||
.getNameWithoutPackage())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli.compiler.autoconfigure;
|
||||
|
||||
import org.codehaus.groovy.ast.ClassNode;
|
||||
import org.codehaus.groovy.control.customizers.ImportCustomizer;
|
||||
import org.springframework.bootstrap.cli.compiler.AstUtils;
|
||||
import org.springframework.bootstrap.cli.compiler.CompilerAutoConfiguration;
|
||||
import org.springframework.bootstrap.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.ifMissingClasses("org.springframework.web.servlet.mvc.Controller")
|
||||
.add("org.springframework", "spring-webmvc", "4.0.0.BOOTSTRAP-SNAPSHOT");
|
||||
|
||||
dependencies.ifMissingClasses("org.apache.catalina.startup.Tomcat",
|
||||
"org.eclipse.jetty.server.Server").add("org.eclipse.jetty",
|
||||
"jetty-webapp", "8.1.10.v20130312");
|
||||
|
||||
// FIXME restore Tomcat when we can get reload to work
|
||||
// dependencies.ifMissingClasses("org.apache.catalina.startup.Tomcat",
|
||||
// "org.eclipse.jetty.server.Server")
|
||||
// .add("org.apache.tomcat.embed", "tomcat-embed-core", "7.0.37")
|
||||
// .add("org.apache.tomcat.embed", "tomcat-embed-logging-juli", "7.0.37");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(ClassNode classNode) {
|
||||
return AstUtils.hasLeastOneAnnotation(classNode, "Controller", "EnableWebMvc");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyImports(ImportCustomizer imports) {
|
||||
imports.addStarImports("org.springframework.web.bind.annotation",
|
||||
"org.springframework.web.servlet.config.annotation");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.cli.runner;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.springframework.bootstrap.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
|
||||
*/
|
||||
public class BootstrapRunner {
|
||||
|
||||
// FIXME logging
|
||||
|
||||
private BootstrapRunnerConfiguration configuration;
|
||||
|
||||
private final File file;
|
||||
|
||||
private final String[] args;
|
||||
|
||||
private final GroovyCompiler compiler;
|
||||
|
||||
private RunThread runThread;
|
||||
|
||||
private FileWatchThread fileWatchThread;
|
||||
|
||||
/**
|
||||
* Create a new {@link BootstrapRunner} instance.
|
||||
* @param configuration the configuration
|
||||
* @param file the file to compile/watch
|
||||
* @param args input arguments
|
||||
*/
|
||||
public BootstrapRunner(final BootstrapRunnerConfiguration configuration, File file,
|
||||
String... args) {
|
||||
this.configuration = configuration;
|
||||
this.file = file;
|
||||
this.args = args;
|
||||
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
|
||||
*/
|
||||
public synchronized void compileAndRun() throws Exception {
|
||||
try {
|
||||
|
||||
// Shutdown gracefully any running container
|
||||
if (this.runThread != null) {
|
||||
this.runThread.shutdown();
|
||||
this.runThread = null;
|
||||
}
|
||||
|
||||
// Compile
|
||||
Class<?>[] classes = this.compiler.compile(this.file);
|
||||
if (classes.length == 0) {
|
||||
throw new RuntimeException("No classes found in '" + this.file + "'");
|
||||
}
|
||||
|
||||
// Run in new thread to ensure that the context classloader is setup
|
||||
this.runThread = new RunThread(classes);
|
||||
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 Class<?>[] classes;
|
||||
|
||||
private Object applicationContext;
|
||||
|
||||
/**
|
||||
* Create a new {@link RunThread} instance.
|
||||
* @param classes the classes to launch
|
||||
*/
|
||||
public RunThread(Class<?>... classes) {
|
||||
this.classes = classes;
|
||||
if (classes.length != 0) {
|
||||
setContextClassLoader(classes[0].getClassLoader());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// User reflection to load and call Spring
|
||||
Class<?> application = getContextClassLoader().loadClass(
|
||||
"org.springframework.bootstrap.SpringApplication");
|
||||
Method method = application.getMethod("run", Object[].class,
|
||||
String[].class);
|
||||
this.applicationContext = method.invoke(null, this.classes,
|
||||
BootstrapRunner.this.args);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the thread, closing any previously opened appplication 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() {
|
||||
this.previous = BootstrapRunner.this.file.lastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
|
||||
long current = BootstrapRunner.this.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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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.bootstrap.cli.runner;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.springframework.bootstrap.cli.compiler.GroovyCompilerConfiguration;
|
||||
|
||||
/**
|
||||
* Configuration for the {@link BootstrapRunner}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public interface BootstrapRunnerConfiguration 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();
|
||||
|
||||
}
|
||||
0
spring-bootstrap-cli/src/main/resources/.gitignore
vendored
Normal file
0
spring-bootstrap-cli/src/main/resources/.gitignore
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
package org.springframework.bootstrap.cli;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.willThrow;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootstrapCli}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class SpringBootstrapCliTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private SpringBootstrapCli cli;
|
||||
|
||||
@Mock
|
||||
private Command regularCommand;
|
||||
|
||||
@Mock
|
||||
private Command optionCommand;
|
||||
|
||||
private Set<Call> calls = EnumSet.noneOf(Call.class);
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.cli = new SpringBootstrapCli() {
|
||||
|
||||
@Override
|
||||
protected void showUsage() {
|
||||
SpringBootstrapCliTests.this.calls.add(Call.SHOW_USAGE);
|
||||
super.showUsage();
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void errorMessage(String message) {
|
||||
SpringBootstrapCliTests.this.calls.add(Call.ERROR_MESSAGE);
|
||||
super.errorMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void printStackTrace(Exception ex) {
|
||||
SpringBootstrapCliTests.this.calls.add(Call.PRINT_STACK_TRACE);
|
||||
super.printStackTrace(ex);
|
||||
}
|
||||
};
|
||||
given(this.regularCommand.getName()).willReturn("command");
|
||||
given(this.regularCommand.getDescription()).willReturn("A regular command");
|
||||
given(this.optionCommand.getName()).willReturn("option");
|
||||
given(this.optionCommand.getDescription()).willReturn("An optional command");
|
||||
given(this.optionCommand.isOptionCommand()).willReturn(true);
|
||||
this.cli.setCommands(Arrays.asList(this.regularCommand, this.optionCommand));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runWithoutArguments() throws Exception {
|
||||
this.thrown.expect(NoArgumentsException.class);
|
||||
this.cli.run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runCommand() throws Exception {
|
||||
this.cli.run("command", "--arg1", "arg2");
|
||||
verify(this.regularCommand).run("--arg1", "arg2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runOptionCommand() throws Exception {
|
||||
this.cli.run("--option", "--arg1", "arg2");
|
||||
verify(this.optionCommand).run("--arg1", "arg2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runOptionCommandWithoutOption() throws Exception {
|
||||
this.cli.run("option", "--arg1", "arg2");
|
||||
verify(this.optionCommand).run("--arg1", "arg2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runOptionOnNonOptionCommand() throws Exception {
|
||||
this.thrown.expect(NoSuchOptionException.class);
|
||||
this.cli.run("--command", "--arg1", "arg2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingCommand() throws Exception {
|
||||
this.thrown.expect(NoSuchCommandException.class);
|
||||
this.cli.run("missing");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlesSuccess() throws Exception {
|
||||
int status = this.cli.runAndHandleErrors("--option");
|
||||
assertThat(status, equalTo(0));
|
||||
assertThat(this.calls, equalTo((Set<Call>) EnumSet.noneOf(Call.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlesNoArgumentsException() throws Exception {
|
||||
int status = this.cli.runAndHandleErrors();
|
||||
assertThat(status, equalTo(1));
|
||||
assertThat(this.calls, equalTo((Set<Call>) EnumSet.of(Call.SHOW_USAGE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlesNoSuchOptionException() throws Exception {
|
||||
int status = this.cli.runAndHandleErrors("--missing");
|
||||
assertThat(status, equalTo(1));
|
||||
assertThat(this.calls,
|
||||
equalTo((Set<Call>) EnumSet.of(Call.ERROR_MESSAGE, Call.SHOW_USAGE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlesRegularException() throws Exception {
|
||||
willThrow(new RuntimeException()).given(this.regularCommand).run();
|
||||
int status = this.cli.runAndHandleErrors("command");
|
||||
assertThat(status, equalTo(1));
|
||||
assertThat(this.calls, equalTo((Set<Call>) EnumSet.of(Call.ERROR_MESSAGE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlesExceptionWithDashD() throws Exception {
|
||||
willThrow(new RuntimeException()).given(this.regularCommand).run();
|
||||
int status = this.cli.runAndHandleErrors("command", "-d");
|
||||
assertThat(status, equalTo(1));
|
||||
assertThat(this.calls, equalTo((Set<Call>) EnumSet.of(Call.ERROR_MESSAGE,
|
||||
Call.PRINT_STACK_TRACE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlesExceptionWithDashDashDebug() throws Exception {
|
||||
willThrow(new RuntimeException()).given(this.regularCommand).run();
|
||||
int status = this.cli.runAndHandleErrors("command", "--debug");
|
||||
assertThat(status, equalTo(1));
|
||||
assertThat(this.calls, equalTo((Set<Call>) EnumSet.of(Call.ERROR_MESSAGE,
|
||||
Call.PRINT_STACK_TRACE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exceptionMessages() throws Exception {
|
||||
assertThat(new NoSuchOptionException("name").getMessage(),
|
||||
equalTo("Unknown option: --name"));
|
||||
assertThat(new NoSuchCommandException("name").getMessage(),
|
||||
equalTo("spr: 'name' is not a valid command. See 'spr --help'."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void help() throws Exception {
|
||||
this.cli.run("help", "command");
|
||||
verify(this.regularCommand).printHelp((PrintStream) anyObject());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void helpNoCommand() throws Exception {
|
||||
this.thrown.expect(NoHelpCommandArgumentsException.class);
|
||||
this.cli.run("help");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void helpUnknownCommand() throws Exception {
|
||||
this.thrown.expect(NoSuchCommandException.class);
|
||||
this.cli.run("help", "missing");
|
||||
}
|
||||
|
||||
private static enum Call {
|
||||
SHOW_USAGE, ERROR_MESSAGE, PRINT_STACK_TRACE
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user