Revert "Rework BootRun so that it does not subclass JavaExec"

This reverts commit 6eee9de3c1.

Closes gh-10872
This commit is contained in:
Andy Wilkinson 2017-11-22 17:20:53 +00:00
parent 6f97bc58a6
commit ffca60d308
17 changed files with 37 additions and 283 deletions

View File

@ -8,11 +8,16 @@ To run your application without first building an archive use the `bootRun` task
$ ./gradlew bootRun $ ./gradlew bootRun
---- ----
The `bootRun` task is automatically configured to use the runtime classpath of the The `bootRun` task is an instance of
main source set. By default, the main class will be discovered by looking for a class {boot-run-javadoc}[`BootRun`] which is a `JavaExec` subclass. As such, all of the
with a `public static void main(String[])` method in directories on the task's {gradle-dsl}/org.gradle.api.tasks.JavaExec.html[usual configuration options] for executing
classpath. The main class can also be configured explicitly using the task's a Java process in Gradle are available to you. The task is automatically configured to use
`mainClassName` property: the runtime classpath of the main source set.
By default, the main class will be configured automatically by looking for a class with a
`public static void main(String[])` method in directories on the task's classpath.
The main class can also be configured explicitly using the task's `main` property:
[source,groovy,indent=0,subs="verbatim"] [source,groovy,indent=0,subs="verbatim"]
---- ----
@ -27,7 +32,7 @@ Alternatively, the main class name can be configured project-wide using the
include::../gradle/running/spring-boot-dsl-main-class-name.gradle[tags=main-class] include::../gradle/running/spring-boot-dsl-main-class-name.gradle[tags=main-class]
---- ----
If the {application-plugin}[`application` plugin] has been applied its `mainClassName` If the {application-plugin}[`application` plugin] has been applied, its `mainClassName`
project property can be used for the same purpose: project property can be used for the same purpose:
[source,groovy,indent=0,subs="verbatim"] [source,groovy,indent=0,subs="verbatim"]
@ -35,15 +40,6 @@ project property can be used for the same purpose:
include::../gradle/running/application-plugin-main-class-name.gradle[tags=main-class] include::../gradle/running/application-plugin-main-class-name.gradle[tags=main-class]
---- ----
Two properties, `args` and `jvmArgs`, are also provided for configuring the
arguments and JVM arguments that are used to run the application.
For more advanced configuration the `JavaExecSpec` that is used can be customized:
[source,groovy,indent=0,subs="verbatim"]
----
include::../gradle/running/boot-run-custom-exec-spec.gradle[tags=customization]
----
[[running-your-application-reloading-resources]] [[running-your-application-reloading-resources]]

View File

@ -14,6 +14,6 @@ mainClassName = 'com.example.ExampleApplication'
task configuredMainClass { task configuredMainClass {
doLast { doLast {
println bootRun.mainClassName println bootRun.main
} }
} }

View File

@ -1,16 +0,0 @@
buildscript {
dependencies {
classpath files(pluginClasspath.split(','))
}
}
apply plugin: 'org.springframework.boot'
apply plugin: 'java'
// tag::customization[]
bootRun {
execSpec {
systemProperty 'com.example.foo', 'bar'
}
}
// end::customization[]

View File

@ -9,12 +9,12 @@ apply plugin: 'java'
// tag::main[] // tag::main[]
bootRun { bootRun {
mainClassName = 'com.example.ExampleApplication' main = 'com.example.ExampleApplication'
} }
// end::main[] // end::main[]
task configuredMainClass { task configuredMainClass {
doLast { doLast {
println bootRun.mainClassName println bootRun.main
} }
} }

View File

@ -16,6 +16,6 @@ springBoot {
task configuredMainClass { task configuredMainClass {
doLast { doLast {
println bootRun.mainClassName println bootRun.main
} }
} }

View File

@ -102,7 +102,6 @@ final class JavaPluginAction implements PluginApplicationAction {
this.singlePublishedArtifact.addCandidate(artifact); this.singlePublishedArtifact.addCandidate(artifact);
} }
@SuppressWarnings("unchecked")
private void configureBootRunTask(Project project) { private void configureBootRunTask(Project project) {
JavaPluginConvention javaConvention = project.getConvention() JavaPluginConvention javaConvention = project.getConvention()
.getPlugin(JavaPluginConvention.class); .getPlugin(JavaPluginConvention.class);
@ -111,14 +110,14 @@ final class JavaPluginAction implements PluginApplicationAction {
run.setGroup(ApplicationPlugin.APPLICATION_GROUP); run.setGroup(ApplicationPlugin.APPLICATION_GROUP);
run.classpath(javaConvention.getSourceSets() run.classpath(javaConvention.getSourceSets()
.findByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath()); .findByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath());
run.setJvmArgs(project.provider(() -> { run.getConventionMapping().map("jvmArgs", () -> {
if (project.hasProperty("applicationDefaultJvmArgs")) { if (project.hasProperty("applicationDefaultJvmArgs")) {
return (List<String>) project.property("applicationDefaultJvmArgs"); return project.property("applicationDefaultJvmArgs");
} }
return Collections.emptyList(); return Collections.emptyList();
})); });
run.setMainClassName( run.conventionMapping("main",
project.provider(new MainClassConvention(project, run::getClasspath))); new MainClassConvention(project, run::getClasspath));
} }
private void configureUtf8Encoding(Project project) { private void configureUtf8Encoding(Project project) {

View File

@ -33,7 +33,7 @@ import org.springframework.boot.loader.tools.MainClassFinder;
* *
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
final class MainClassConvention implements Callable<String> { final class MainClassConvention implements Callable<Object> {
private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication"; private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication";
@ -47,7 +47,7 @@ final class MainClassConvention implements Callable<String> {
} }
@Override @Override
public String call() throws Exception { public Object call() throws Exception {
SpringBootExtension springBootExtension = this.project.getExtensions() SpringBootExtension springBootExtension = this.project.getExtensions()
.findByType(SpringBootExtension.class); .findByType(SpringBootExtension.class);
if (springBootExtension != null if (springBootExtension != null
@ -57,7 +57,7 @@ final class MainClassConvention implements Callable<String> {
if (this.project.hasProperty("mainClassName")) { if (this.project.hasProperty("mainClassName")) {
Object mainClassName = this.project.property("mainClassName"); Object mainClassName = this.project.property("mainClassName");
if (mainClassName != null) { if (mainClassName != null) {
return mainClassName.toString(); return mainClassName;
} }
} }
return resolveMainClass(); return resolveMainClass();

View File

@ -16,21 +16,10 @@
package org.springframework.boot.gradle.tasks.run; package org.springframework.boot.gradle.tasks.run;
import java.util.ArrayList;
import java.util.List;
import org.gradle.api.Action;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.SourceDirectorySet; import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.provider.PropertyState;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.JavaExec; import org.gradle.api.tasks.JavaExec;
import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetOutput; import org.gradle.api.tasks.SourceSetOutput;
import org.gradle.api.tasks.TaskAction;
import org.gradle.process.JavaExecSpec;
/** /**
* Custom {@link JavaExec} task for running a Spring Boot application. * Custom {@link JavaExec} task for running a Spring Boot application.
@ -38,35 +27,7 @@ import org.gradle.process.JavaExecSpec;
* @author Andy Wilkinson * @author Andy Wilkinson
* @since 2.0.0 * @since 2.0.0
*/ */
public class BootRun extends DefaultTask { public class BootRun extends JavaExec {
private final PropertyState<String> mainClassName = getProject()
.property(String.class);
@SuppressWarnings("unchecked")
private final PropertyState<List<String>> jvmArgs = (PropertyState<List<String>>) (Object) getProject()
.property(List.class);
@SuppressWarnings("unchecked")
private final PropertyState<List<String>> args = (PropertyState<List<String>>) (Object) getProject()
.property(List.class);
private FileCollection classpath = getProject().files();
private List<Action<JavaExecSpec>> execSpecConfigurers = new ArrayList<>();
/**
* Adds the given {@code entries} to the classpath used to run the application.
* @param entries the classpath entries
*/
public void classpath(Object... entries) {
this.classpath = this.classpath.plus(getProject().files(entries));
}
@InputFiles
public FileCollection getClasspath() {
return this.classpath;
}
/** /**
* Adds the {@link SourceDirectorySet#getSrcDirs() source directories} of the given * Adds the {@link SourceDirectorySet#getSrcDirs() source directories} of the given
@ -77,116 +38,18 @@ public class BootRun extends DefaultTask {
* @param sourceSet the source set * @param sourceSet the source set
*/ */
public void sourceResources(SourceSet sourceSet) { public void sourceResources(SourceSet sourceSet) {
this.classpath = getProject() setClasspath(getProject()
.files(sourceSet.getResources().getSrcDirs(), this.classpath) .files(sourceSet.getResources().getSrcDirs(), getClasspath())
.filter((file) -> !file.equals(sourceSet.getOutput().getResourcesDir())); .filter((file) -> !file.equals(sourceSet.getOutput().getResourcesDir())));
} }
/** @Override
* Returns the name of the main class to be run. public void exec() {
* @return the main class name or {@code null} if (System.console() != null) {
*/ // Record that the console is available here for AnsiOutput to detect later
public String getMainClassName() { this.getEnvironment().put("spring.output.ansi.console-available", true);
return this.mainClassName.getOrNull(); }
} super.exec();
/**
* Sets the name of the main class to be executed using the given
* {@code mainClassNameProvider}.
*
* @param mainClassNameProvider provider of the main class name
*/
public void setMainClassName(Provider<String> mainClassNameProvider) {
this.mainClassName.set(mainClassNameProvider);
}
/**
* Sets the name of the main class to be run.
*
* @param mainClassName the main class name
*/
public void setMainClassName(String mainClassName) {
this.mainClassName.set(mainClassName);
}
/**
* Returns the JVM arguments to be used to run the application.
* @return the JVM arguments or {@code null}
*/
public List<String> getJvmArgs() {
return this.jvmArgs.getOrNull();
}
/**
* Configures the application to be run using the JVM args provided by the given
* {@code jvmArgsProvider}.
*
* @param jvmArgsProvider the provider of the JVM args
*/
public void setJvmArgs(Provider<List<String>> jvmArgsProvider) {
this.jvmArgs.set(jvmArgsProvider);
}
/**
* Configures the application to be run using the given {@code jvmArgs}.
* @param jvmArgs the JVM args
*/
public void setJvmArgs(List<String> jvmArgs) {
this.jvmArgs.set(jvmArgs);
}
/**
* Returns the arguments to be used to run the application.
* @return the arguments or {@code null}
*/
public List<String> getArgs() {
return this.args.getOrNull();
}
/**
* Configures the application to be run using the given {@code args}.
* @param args the args
*/
public void setArgs(List<String> args) {
this.args.set(args);
}
/**
* Configures the application to be run using the args provided by the given
* {@code argsProvider}.
* @param argsProvider the provider of the args
*/
public void setArgs(Provider<List<String>> argsProvider) {
this.args.set(argsProvider);
}
/**
* Registers the given {@code execSpecConfigurer} to be called to customize the
* {@link JavaExecSpec} prior to running the application.
* @param execSpecConfigurer the configurer
*/
public void execSpec(Action<JavaExecSpec> execSpecConfigurer) {
this.execSpecConfigurers.add(execSpecConfigurer);
}
@TaskAction
public void run() {
getProject().javaexec((spec) -> {
spec.classpath(this.classpath);
spec.setMain(this.mainClassName.getOrNull());
if (this.jvmArgs.isPresent()) {
spec.setJvmArgs(this.jvmArgs.get());
}
if (this.args.isPresent()) {
spec.setArgs(this.args.get());
}
if (System.console() != null) {
// Record that the console is available here for AnsiOutput to detect
// later
spec.environment("spring.output.ansi.console-available", true);
}
this.execSpecConfigurers.forEach((configurer) -> configurer.execute(spec));
});
} }
} }

View File

@ -18,7 +18,6 @@ package com.example;
import java.io.File; import java.io.File;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
import java.util.Arrays;
/** /**
* Very basic application used for testing {@code BootRun}. * Very basic application used for testing {@code BootRun}.
@ -32,12 +31,6 @@ public class BootRunApplication {
} }
public static void main(String[] args) { public static void main(String[] args) {
dumpClassPath();
dumpArgs(args);
dumpJvmArgs();
}
private static void dumpClassPath() {
int i = 1; int i = 1;
for (String entry : ManagementFactory.getRuntimeMXBean().getClassPath() for (String entry : ManagementFactory.getRuntimeMXBean().getClassPath()
.split(File.pathSeparator)) { .split(File.pathSeparator)) {
@ -45,12 +38,4 @@ public class BootRunApplication {
} }
} }
private static void dumpArgs(String[] args) {
System.out.println(Arrays.toString(args));
}
private static void dumpJvmArgs() {
System.out.println(ManagementFactory.getRuntimeMXBean().getInputArguments());
}
} }

View File

@ -67,11 +67,4 @@ public class RunningDocumentationTests {
.contains(new File("src/main/resources").getPath()); .contains(new File("src/main/resources").getPath());
} }
@Test
public void bootRunExecSpecCustomization() throws IOException {
this.gradleBuild
.script("src/main/gradle/running/boot-run-custom-exec-spec.gradle")
.build();
}
} }

View File

@ -56,33 +56,6 @@ public class BootRunIntegrationTests {
.doesNotContain(canonicalPathOf("src/main/resources")); .doesNotContain(canonicalPathOf("src/main/resources"));
} }
@Test
public void argsCanBeConfigured() throws IOException {
copyApplication();
new File(this.gradleBuild.getProjectDir(), "src/main/resources").mkdirs();
BuildResult result = this.gradleBuild.build("bootRun");
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()).contains("--com.example.foo=bar");
}
@Test
public void jvmArgsCanBeConfigured() throws IOException {
copyApplication();
new File(this.gradleBuild.getProjectDir(), "src/main/resources").mkdirs();
BuildResult result = this.gradleBuild.build("bootRun");
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()).contains("-Dcom.example.foo=bar");
}
@Test
public void execSpecCanBeConfigured() throws IOException {
copyApplication();
new File(this.gradleBuild.getProjectDir(), "src/main/resources").mkdirs();
BuildResult result = this.gradleBuild.build("bootRun");
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()).contains("-Dcom.example.foo=bar");
}
@Test @Test
public void sourceResourcesCanBeUsed() throws IOException { public void sourceResourcesCanBeUsed() throws IOException {
copyApplication(); copyApplication();

View File

@ -10,6 +10,6 @@ apply plugin: 'org.springframework.boot'
task echoMainClassName { task echoMainClassName {
dependsOn compileJava dependsOn compileJava
doLast { doLast {
println 'Main class name = ' + bootRun.mainClassName println 'Main class name = ' + bootRun.main
} }
} }

View File

@ -10,5 +10,5 @@ apply plugin: 'org.springframework.boot'
mainClassName = 'com.example.CustomMainClass' mainClassName = 'com.example.CustomMainClass'
task echoMainClassName { task echoMainClassName {
println 'Main class name = ' + bootRun.mainClassName println 'Main class name = ' + bootRun.main
} }

View File

@ -1,12 +0,0 @@
buildscript {
dependencies {
classpath files(pluginClasspath.split(','))
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
bootRun {
args = ['--com.example.foo=bar']
}

View File

@ -1,15 +0,0 @@
buildscript {
dependencies {
classpath files(pluginClasspath.split(','))
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
bootRun {
execSpec {
systemProperty 'com.example.foo', 'bar'
}
}

View File

@ -1,12 +0,0 @@
buildscript {
dependencies {
classpath files(pluginClasspath.split(','))
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
bootRun {
jvmArgs = ['-Dcom.example.foo=bar']
}

View File

@ -12,5 +12,5 @@ springBoot {
} }
task echoMainClassName { task echoMainClassName {
println 'Main class name = ' + bootRun.mainClassName println 'Main class name = ' + bootRun.main
} }