diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/running.adoc b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/running.adoc index 277fd47355..164792db46 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/running.adoc +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/asciidoc/running.adoc @@ -8,11 +8,10 @@ To run your application without first building an archive use the `bootRun` task $ ./gradlew bootRun ---- -The `bootRun` task is an instance of -{boot-run-javadoc}[`BootRun`] which is a `JavaExec` subclass. As such, all of the -{gradle-dsl}/org.gradle.api.tasks.JavaExec.html[usual configuration options] for executing -a Java process in Gradle are available to you. The task is automatically configured to use -the runtime classpath of the main source set. +The `bootRun` task is an instance of {boot-run-javadoc}[`BootRun`] which is a `JavaExec` +subclass. As such, all of the {gradle-dsl}/org.gradle.api.tasks.JavaExec.html[usual +configuration options] for executing a Java process in Gradle are available to you. The +task is automatically configured to use 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. @@ -48,6 +47,23 @@ include::../gradle/running/spring-boot-dsl-main-class-name.gradle.kts[tags=main- ---- +By default, `bootRun` will configure the JVM to optimize its launch for faster startup +during development. This behavior can be disabled by using the `optimizedLaunch` property, +as shown in the following example: + +[source,groovy,indent=0,subs="verbatim,attributes",role="primary"] +.Groovy +---- +include::../gradle/running/boot-run-disable-optimized-launch.gradle[tags=launch] +---- + +[source,kotlin,indent=0,subs="verbatim,attributes",role="secondary"] +.Kotlin +---- +include::../gradle/running/boot-run-disable-optimized-launch.gradle.kts[tags=launch] +---- + + If the {application-plugin}[`application` plugin] has been applied, its `mainClassName` project property must be configured and can be used for the same purpose: diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/boot-run-disable-optimized-launch.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/boot-run-disable-optimized-launch.gradle new file mode 100644 index 0000000000..2c6b6499b1 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/boot-run-disable-optimized-launch.gradle @@ -0,0 +1,16 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '{version}' +} + +// tag::launch[] +bootRun { + optimizedLaunch = false +} +// end::launch[] + +task optimizedLaunch { + doLast { + println bootRun.optimizedLaunch + } +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/boot-run-disable-optimized-launch.gradle.kts b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/boot-run-disable-optimized-launch.gradle.kts new file mode 100644 index 0000000000..ac80c50b3a --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/gradle/running/boot-run-disable-optimized-launch.gradle.kts @@ -0,0 +1,18 @@ +import org.springframework.boot.gradle.tasks.run.BootRun + +plugins { + java + id("org.springframework.boot") version "{version}" +} + +// tag::launch[] +tasks.getByName("bootRun") { + isOptimizedLaunch = false +} +// end::launch[] + +task("optimizedLaunch") { + doLast { + println(tasks.getByName("bootRun").isOptimizedLaunch) + } +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/BootRun.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/BootRun.java index 351b548130..f204e2b8e8 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/BootRun.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/run/BootRun.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -17,6 +17,7 @@ package org.springframework.boot.gradle.tasks.run; import org.gradle.api.file.SourceDirectorySet; +import org.gradle.api.tasks.Input; import org.gradle.api.tasks.JavaExec; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.SourceSetOutput; @@ -29,6 +30,29 @@ import org.gradle.api.tasks.SourceSetOutput; */ public class BootRun extends JavaExec { + private boolean optimizedLaunch = true; + + /** + * Returns {@code true} if the JVM's launch should be optimized, otherwise + * {@code false}. Defaults to {@code true}. + * @return whether the JVM's launch should be optimized + * @since 2.2.0 + */ + @Input + public boolean isOptimizedLaunch() { + return this.optimizedLaunch; + } + + /** + * Sets whether the JVM's launch should be optimized. Defaults to {@code true}. + * @param optimizedLaunch {@code true} if the JVM's launch should be optimised, + * otherwise {@code false} + * @since 2.2.0 + */ + public void setOptimizedLaunch(boolean optimizedLaunch) { + this.optimizedLaunch = optimizedLaunch; + } + /** * Adds the {@link SourceDirectorySet#getSrcDirs() source directories} of the given * {@code sourceSet's} {@link SourceSet#getResources() resources} to the start of the @@ -44,6 +68,10 @@ public class BootRun extends JavaExec { @Override public void exec() { + if (this.optimizedLaunch) { + setJvmArgs(getJvmArgs()); + jvmArgs("-Xverify:none", "-XX:TieredStopAtLevel=1"); + } if (System.console() != null) { // Record that the console is available here for AnsiOutput to detect later this.getEnvironment().put("spring.output.ansi.console-available", true); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/com/example/BootRunApplication.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/com/example/classpath/BootRunClasspathApplication.java similarity index 78% rename from spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/com/example/BootRunApplication.java rename to spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/com/example/classpath/BootRunClasspathApplication.java index 34ce55940f..fa1f3cad36 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/com/example/BootRunApplication.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/com/example/classpath/BootRunClasspathApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2019 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. @@ -14,19 +14,19 @@ * limitations under the License. */ -package com.example; +package com.example.classpath; import java.io.File; import java.lang.management.ManagementFactory; /** - * Very basic application used for testing {@code BootRun}. + * Application used for testing {@code BootRun}'s classpath handling. * * @author Andy Wilkinson */ -public class BootRunApplication { +public class BootRunClasspathApplication { - protected BootRunApplication() { + protected BootRunClasspathApplication() { } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/com/example/jvmargs/BootRunJvmArgsApplication.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/com/example/jvmargs/BootRunJvmArgsApplication.java new file mode 100644 index 0000000000..f3b111f716 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/com/example/jvmargs/BootRunJvmArgsApplication.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-2019 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 + * + * https://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 com.example.jvmargs; + +import java.lang.management.ManagementFactory; + +/** + * Application used for testing {@code BootRun}'s JVM argument handling. + * + * @author Andy Wilkinson + */ +public class BootRunJvmArgsApplication { + + protected BootRunJvmArgsApplication() { + + } + + public static void main(String[] args) { + int i = 1; + for (String entry : ManagementFactory.getRuntimeMXBean().getInputArguments()) { + System.out.println(i++ + ". " + entry); + } + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/RunningDocumentationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/RunningDocumentationTests.java index 471fa9f9c8..c5de622e50 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/RunningDocumentationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/docs/RunningDocumentationTests.java @@ -69,4 +69,11 @@ public class RunningDocumentationTests { .contains(new File("src/main/resources").getPath()); } + @TestTemplate + public void bootRunDisableOptimizedLaunch() throws IOException { + assertThat(this.gradleBuild + .script("src/main/gradle/running/boot-run-disable-optimized-launch") + .build("optimizedLaunch").getOutput()).contains("false"); + } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests.java index fc788084a7..f5ec26b8cb 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests.java @@ -42,7 +42,7 @@ public class BootRunIntegrationTests { @TestTemplate public void basicExecution() throws IOException { - copyApplication(); + copyClasspathApplication(); new File(this.gradleBuild.getProjectDir(), "src/main/resources").mkdirs(); BuildResult result = this.gradleBuild.build("bootRun"); assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); @@ -56,7 +56,7 @@ public class BootRunIntegrationTests { @TestTemplate public void sourceResourcesCanBeUsed() throws IOException { - copyApplication(); + copyClasspathApplication(); BuildResult result = this.gradleBuild.build("bootRun"); assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); assertThat(result.getOutput()) @@ -87,28 +87,56 @@ public class BootRunIntegrationTests { @TestTemplate public void applicationPluginMainClassNameIsNotUsedWhenItIsNull() throws IOException { - copyApplication(); + copyClasspathApplication(); BuildResult result = this.gradleBuild.build("echoMainClassName"); assertThat(result.task(":echoMainClassName").getOutcome()) .isEqualTo(TaskOutcome.SUCCESS); - assertThat(result.getOutput()) - .contains("Main class name = com.example.BootRunApplication"); + assertThat(result.getOutput()).contains( + "Main class name = com.example.classpath.BootRunClasspathApplication"); + } + + @TestTemplate + public void defaultJvmArgs() throws IOException { + copyJvmArgsApplication(); + BuildResult result = this.gradleBuild.build("bootRun"); + assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(result.getOutput()).contains("1. -Xverify:none") + .contains("2. -XX:TieredStopAtLevel=1"); + } + + @TestTemplate + public void optimizedLaunchDisabledJvmArgs() throws IOException { + copyJvmArgsApplication(); + BuildResult result = this.gradleBuild.build("bootRun"); + assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(result.getOutput()).doesNotContain("-Xverify:none") + .doesNotContain("-XX:TieredStopAtLevel=1"); } @TestTemplate public void applicationPluginJvmArgumentsAreUsed() throws IOException { - BuildResult result = this.gradleBuild.build("echoJvmArguments"); - assertThat(result.task(":echoJvmArguments").getOutcome()) - .isEqualTo(TaskOutcome.UP_TO_DATE); - assertThat(result.getOutput()) - .contains("JVM arguments = [-Dcom.foo=bar, -Dcom.bar=baz]"); + copyJvmArgsApplication(); + BuildResult result = this.gradleBuild.build("bootRun"); + assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + assertThat(result.getOutput()).contains("1. -Dcom.bar=baz") + .contains("2. -Dcom.foo=bar").contains("3. -Xverify:none") + .contains("4. -XX:TieredStopAtLevel=1"); } - private void copyApplication() throws IOException { + private void copyClasspathApplication() throws IOException { + copyApplication("classpath"); + } + + private void copyJvmArgsApplication() throws IOException { + copyApplication("jvmargs"); + } + + private void copyApplication(String name) throws IOException { File output = new File(this.gradleBuild.getProjectDir(), - "src/main/java/com/example"); + "src/main/java/com/example/" + name); output.mkdirs(); - FileSystemUtils.copyRecursively(new File("src/test/java/com/example"), output); + FileSystemUtils.copyRecursively(new File("src/test/java/com/example/" + name), + output); } private String canonicalPathOf(String path) throws IOException { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle index bdba964355..9db3ab19b0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle @@ -4,7 +4,3 @@ plugins { } applicationDefaultJvmArgs = ['-Dcom.foo=bar', '-Dcom.bar=baz'] - -task echoJvmArguments { - println 'JVM arguments = ' + bootRun.jvmArgs -} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-defaultJvmArgs.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-defaultJvmArgs.gradle new file mode 100644 index 0000000000..e351e2cbb9 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-defaultJvmArgs.gradle @@ -0,0 +1,4 @@ +plugins { + id 'application' + id 'org.springframework.boot' version '{version}' +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-optimizedLaunchDisabledJvmArgs.gradle b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-optimizedLaunchDisabledJvmArgs.gradle new file mode 100644 index 0000000000..c4308f56a2 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/tasks/run/BootRunIntegrationTests-optimizedLaunchDisabledJvmArgs.gradle @@ -0,0 +1,8 @@ +plugins { + id 'application' + id 'org.springframework.boot' version '{version}' +} + +bootRun { + optimizedLaunch = false +}