mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-07-05 00:56:58 +08:00
[bs-24] Support multi-file compile from command line
* Each non-option arg is tested to see if it is a file * If it is .groovy (or .java) it is compiled [#48127661]
This commit is contained in:
parent
a5f7b6ead3
commit
bb62ca835e
|
@ -18,6 +18,7 @@ package org.springframework.bootstrap.cli;
|
|||
|
||||
import java.awt.Desktop;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
|
@ -31,9 +32,11 @@ import org.springframework.bootstrap.cli.runner.BootstrapRunnerConfiguration;
|
|||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* {@link Command} to 'run' a spring groovy script.
|
||||
* {@link Command} to 'run' a groovy script or scripts.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @see BootstrapRunner
|
||||
*/
|
||||
public class RunCommand extends OptionParsingCommand {
|
||||
|
@ -58,7 +61,7 @@ public class RunCommand extends OptionParsingCommand {
|
|||
|
||||
@Override
|
||||
public String getUsageHelp() {
|
||||
return "[options] <file>";
|
||||
return "[options] <files> [--] [args]";
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
|
@ -86,30 +89,40 @@ public class RunCommand extends OptionParsingCommand {
|
|||
@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());
|
||||
File[] files = getFileArguments(nonOptionArguments);
|
||||
List<String> args = nonOptionArguments.subList(files.length,
|
||||
nonOptionArguments.size());
|
||||
|
||||
if (options.has(this.editOption)) {
|
||||
Desktop.getDesktop().edit(file);
|
||||
Desktop.getDesktop().edit(files[0]);
|
||||
}
|
||||
|
||||
BootstrapRunnerConfiguration configuration = new BootstrapRunnerConfigurationAdapter(
|
||||
options);
|
||||
this.runner = new BootstrapRunner(configuration, file,
|
||||
this.runner = new BootstrapRunner(configuration, files,
|
||||
args.toArray(new String[args.size()]));
|
||||
this.runner.compileAndRun();
|
||||
}
|
||||
|
||||
private File getFileArgument(List<String> nonOptionArguments) {
|
||||
if (nonOptionArguments.size() == 0) {
|
||||
private File[] getFileArguments(List<String> nonOptionArguments) {
|
||||
List<File> files = new ArrayList<File>();
|
||||
for (String filename : nonOptionArguments) {
|
||||
if ("--".equals(filename)) {
|
||||
break;
|
||||
}
|
||||
// TODO: add support for strict Java compilation
|
||||
// TODO: add support for recursive search in directory
|
||||
if (filename.endsWith(".groovy") || filename.endsWith(".java")) {
|
||||
File file = new File(filename);
|
||||
if (file.isFile() && file.canRead()) {
|
||||
files.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (files.size() == 0) {
|
||||
throw new RuntimeException("Please specify a file to run");
|
||||
}
|
||||
String filename = nonOptionArguments.get(0);
|
||||
File file = new File(filename);
|
||||
if (!file.isFile() || !file.canRead()) {
|
||||
throw new RuntimeException("Unable to read '" + filename + "'");
|
||||
}
|
||||
return file;
|
||||
return files.toArray(new File[files.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -35,13 +35,16 @@ import org.codehaus.groovy.control.SourceUnit;
|
|||
* 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
|
||||
|
@ -54,8 +57,12 @@ class ExtendedGroovyClassLoader extends GroovyClassLoader {
|
|||
return resourceStream;
|
||||
}
|
||||
|
||||
public CompilerConfiguration getConfiguration() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClassCollector createCollector(CompilationUnit unit, SourceUnit su) {
|
||||
public ClassCollector createCollector(CompilationUnit unit, SourceUnit su) {
|
||||
InnerLoader loader = AccessController
|
||||
.doPrivileged(new PrivilegedAction<InnerLoader>() {
|
||||
@Override
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.bootstrap.cli.compiler;
|
||||
|
||||
import groovy.lang.GroovyClassLoader;
|
||||
import groovy.lang.GroovyClassLoader.ClassCollector;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -26,8 +27,10 @@ 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.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;
|
||||
|
@ -49,6 +52,7 @@ import org.springframework.bootstrap.cli.compiler.autoconfigure.SpringMvcCompile
|
|||
* <ul>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class GroovyCompiler {
|
||||
|
||||
|
@ -79,24 +83,37 @@ public class GroovyCompiler {
|
|||
|
||||
/**
|
||||
* 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).
|
||||
* {@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 {
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
classes.remove(mainClass);
|
||||
classes.add(0, mainClass);
|
||||
|
||||
return classes.toArray(new Class<?>[classes.size()]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -37,7 +37,7 @@ public class BootstrapRunner {
|
|||
|
||||
private BootstrapRunnerConfiguration configuration;
|
||||
|
||||
private final File file;
|
||||
private final File[] files;
|
||||
|
||||
private final String[] args;
|
||||
|
||||
|
@ -50,13 +50,13 @@ public class BootstrapRunner {
|
|||
/**
|
||||
* Create a new {@link BootstrapRunner} instance.
|
||||
* @param configuration the configuration
|
||||
* @param file the file to compile/watch
|
||||
* @param files the files to compile/watch
|
||||
* @param args input arguments
|
||||
*/
|
||||
public BootstrapRunner(final BootstrapRunnerConfiguration configuration, File file,
|
||||
String... args) {
|
||||
public BootstrapRunner(final BootstrapRunnerConfiguration configuration,
|
||||
File[] files, String... args) {
|
||||
this.configuration = configuration;
|
||||
this.file = file;
|
||||
this.files = files;
|
||||
this.args = args;
|
||||
this.compiler = new GroovyCompiler(configuration);
|
||||
if (configuration.getLogLevel().intValue() <= Level.FINE.intValue()) {
|
||||
|
@ -75,9 +75,9 @@ public class BootstrapRunner {
|
|||
stop();
|
||||
|
||||
// Compile
|
||||
Class<?>[] classes = this.compiler.compile(this.file);
|
||||
Class<?>[] classes = this.compiler.compile(this.files);
|
||||
if (classes.length == 0) {
|
||||
throw new RuntimeException("No classes found in '" + this.file + "'");
|
||||
throw new RuntimeException("No classes found in '" + this.files + "'");
|
||||
}
|
||||
|
||||
// Run in new thread to ensure that the context classloader is setup
|
||||
|
@ -164,7 +164,13 @@ public class BootstrapRunner {
|
|||
private long previous;
|
||||
|
||||
public FileWatchThread() {
|
||||
this.previous = BootstrapRunner.this.file.lastModified();
|
||||
this.previous = 0;
|
||||
for (File file : BootstrapRunner.this.files) {
|
||||
long current = file.lastModified();
|
||||
if (current > this.previous) {
|
||||
this.previous = current;
|
||||
}
|
||||
}
|
||||
setDaemon(false);
|
||||
}
|
||||
|
||||
|
@ -173,10 +179,12 @@ public class BootstrapRunner {
|
|||
while (true) {
|
||||
try {
|
||||
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
|
||||
long current = BootstrapRunner.this.file.lastModified();
|
||||
if (this.previous < current) {
|
||||
this.previous = current;
|
||||
compileAndRun();
|
||||
for (File file : BootstrapRunner.this.files) {
|
||||
long current = file.lastModified();
|
||||
if (this.previous < current) {
|
||||
this.previous = current;
|
||||
compileAndRun();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
|
|
|
@ -59,7 +59,7 @@ public class SampleIntegrationTests {
|
|||
return this.output.toString();
|
||||
}
|
||||
|
||||
private void start(final String sample) throws Exception {
|
||||
private void start(final String... sample) throws Exception {
|
||||
Future<RunCommand> future = Executors.newSingleThreadExecutor().submit(
|
||||
new Callable<RunCommand>() {
|
||||
@Override
|
||||
|
@ -94,12 +94,23 @@ public class SampleIntegrationTests {
|
|||
|
||||
@Test
|
||||
public void jobSample() throws Exception {
|
||||
start("samples/job.groovy");
|
||||
start("samples/job.groovy", "foo=bar");
|
||||
String output = getOutput();
|
||||
assertTrue("Wrong output: " + output,
|
||||
output.contains("completed with the following parameters"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jobWebSample() throws Exception {
|
||||
start("samples/job.groovy", "samples/web.groovy", "foo=bar");
|
||||
String output = getOutput();
|
||||
assertTrue("Wrong output: " + output,
|
||||
output.contains("completed with the following parameters"));
|
||||
String result = FileUtil.readEntirely(new URL("http://localhost:8080")
|
||||
.openStream());
|
||||
assertEquals("World!", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void webSample() throws Exception {
|
||||
start("samples/web.groovy");
|
||||
|
|
|
@ -20,11 +20,18 @@ import javax.sql.DataSource;
|
|||
|
||||
import org.springframework.batch.support.DatabaseType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Initialize the Spring Batch schema (ignoring errors, so should be idempotent).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class BatchDatabaseInitializer {
|
||||
|
||||
|
@ -34,6 +41,9 @@ public class BatchDatabaseInitializer {
|
|||
@Autowired
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
@Value("${spring.batch.schema:classpath:org/springframework/batch/core/schema-@@platform@@.sql}")
|
||||
private String schemaLocation = "classpath:org/springframework/batch/core/schema-@@platform@@.sql";
|
||||
|
||||
@PostConstruct
|
||||
protected void initialize() throws Exception {
|
||||
String platform = DatabaseType.fromMetaData(this.dataSource).toString()
|
||||
|
@ -42,12 +52,9 @@ public class BatchDatabaseInitializer {
|
|||
platform = "hsqldb";
|
||||
}
|
||||
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
|
||||
populator
|
||||
.addScript(this.resourceLoader
|
||||
.getResource("org/springframework/batch/core/schema-" + platform
|
||||
+ ".sql"));
|
||||
populator.addScript(this.resourceLoader.getResource(this.schemaLocation.replace(
|
||||
"@@platform@@", platform)));
|
||||
populator.setContinueOnError(true);
|
||||
DatabasePopulatorUtils.execute(populator, this.dataSource);
|
||||
}
|
||||
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.bootstrap.context.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -47,8 +48,17 @@ public abstract class AutoConfigurationUtils {
|
|||
|
||||
public static void storeBasePackages(ConfigurableListableBeanFactory beanFactory,
|
||||
List<String> basePackages) {
|
||||
beanFactory.registerSingleton(BASE_PACKAGES_BEAN,
|
||||
Collections.unmodifiableList(basePackages));
|
||||
if (!beanFactory.containsBean(BASE_PACKAGES_BEAN)) {
|
||||
beanFactory.registerSingleton(BASE_PACKAGES_BEAN, new ArrayList<String>(
|
||||
basePackages));
|
||||
} else {
|
||||
List<String> packages = getBasePackages(beanFactory);
|
||||
for (String pkg : basePackages) {
|
||||
if (packages.contains(pkg)) {
|
||||
packages.add(pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user