diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java index 5baa4a9cbcf..b1a262e61f6 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/bundling/BundlingPluginFeatures.java @@ -19,6 +19,7 @@ package org.springframework.boot.gradle.bundling; import java.util.Collections; import java.util.Set; import java.util.concurrent.Callable; +import java.util.function.Supplier; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; @@ -66,9 +67,8 @@ public class BundlingPluginFeatures implements PluginFeatures { ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootWar); this.singlePublishedArtifact.addCandidate(artifact); project.getComponents().add(new BootSoftwareComponent(artifact, "bootWeb")); - bootWar.conventionMapping("mainClass", () -> { - return new MainClassResolver(bootWar.getClasspath()).resolveMainClass(); - }); + bootWar.conventionMapping("mainClass", + mainClassConvention(project, bootWar::getClasspath)); } private void configureBootJarTask(Project project) { @@ -83,9 +83,18 @@ public class BundlingPluginFeatures implements PluginFeatures { ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootJar); this.singlePublishedArtifact.addCandidate(artifact); project.getComponents().add(new BootSoftwareComponent(artifact, "bootJava")); - bootJar.conventionMapping("mainClass", () -> { - return new MainClassResolver(bootJar.getClasspath()).resolveMainClass(); - }); + bootJar.conventionMapping("mainClass", + mainClassConvention(project, bootJar::getClasspath)); + } + + private Callable mainClassConvention(Project project, + Supplier classpathSupplier) { + return () -> { + if (project.hasProperty("mainClassName")) { + return project.property("mainClassName"); + } + return new MainClassResolver(classpathSupplier.get()).resolveMainClass(); + }; } private void configureBootArchivesUpload(Project project) { diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java index 0ad7034b4f7..c7838b22a45 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/RunPluginFeatures.java @@ -44,26 +44,25 @@ public class RunPluginFeatures implements PluginFeatures { }); } - private void addBootRunTask(final Project project) { - final JavaPluginConvention javaConvention = project.getConvention() + private void addBootRunTask(Project project) { + JavaPluginConvention javaConvention = project.getConvention() .getPlugin(JavaPluginConvention.class); - BootRun run = project.getTasks().create(RUN_APP_TASK_NAME, BootRun.class); run.setDescription("Run the project with support for " + "auto-detecting main class and reloading static resources"); run.setGroup("application"); run.classpath(javaConvention.getSourceSets() .findByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath()); - run.getConventionMapping().map("jvmArgs", new Callable() { - @Override - public Object call() throws Exception { - if (project.hasProperty("applicationDefaultJvmArgs")) { - return project.property("applicationDefaultJvmArgs"); - } - return Collections.emptyList(); + run.getConventionMapping().map("jvmArgs", ((Callable) () -> { + if (project.hasProperty("applicationDefaultJvmArgs")) { + return project.property("applicationDefaultJvmArgs"); } - }); + return Collections.emptyList(); + })); run.conventionMapping("main", () -> { + if (project.hasProperty("mainClassName")) { + return project.property("mainClassName"); + } return new MainClassResolver(run.getClasspath()).resolveMainClass(); }); } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveIntegrationTests.java index 9ab3e301c93..11ad775d9d2 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/bundling/AbstractBootArchiveIntegrationTests.java @@ -16,7 +16,9 @@ package org.springframework.boot.gradle.bundling; +import java.io.File; import java.io.IOException; +import java.util.jar.JarFile; import org.gradle.testkit.runner.InvalidRunnerConfigurationException; import org.gradle.testkit.runner.TaskOutcome; @@ -97,4 +99,17 @@ public abstract class AbstractBootArchiveIntegrationTests { .getOutcome()).isEqualTo(TaskOutcome.SUCCESS); } + @Test + public void applicationPluginMainClassNameIsUsed() throws IOException { + assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName) + .getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + try (JarFile jarFile = new JarFile( + new File(this.gradleBuild.getProjectDir(), "build/libs") + .listFiles()[0])) { + assertThat(jarFile.getManifest().getMainAttributes().getValue("Start-Class")) + .isEqualTo("com.example.CustomMain"); + } + + } + } diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/run/BootRunIntegrationTests.java b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/run/BootRunIntegrationTests.java index 5945b21551f..120a211be7c 100644 --- a/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/run/BootRunIntegrationTests.java +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/run/BootRunIntegrationTests.java @@ -66,6 +66,24 @@ public class BootRunIntegrationTests { assertThat(result.getOutput()).doesNotContain(urlOf("build/resources/main")); } + @Test + public void applicationPluginMainClassNameIsUsed() throws IOException { + BuildResult result = this.gradleBuild.build("echoMainClassName"); + assertThat(result.task(":echoMainClassName").getOutcome()) + .isEqualTo(TaskOutcome.UP_TO_DATE); + assertThat(result.getOutput()) + .contains("Main class name = com.example.CustomMainClass"); + } + + @Test + 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]"); + } + private String urlOf(String path) throws IOException { return new File(this.gradleBuild.getProjectDir().getCanonicalFile(), path).toURI() .toURL().toString(); diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootJarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootJarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle new file mode 100644 index 00000000000..684171c1403 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootJarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle @@ -0,0 +1,11 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'java' +apply plugin: 'org.springframework.boot' +apply plugin: 'application' + +mainClassName = 'com.example.CustomMain' diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootWarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootWarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle new file mode 100644 index 00000000000..8da00e317bf --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/bundling/BootWarIntegrationTests-applicationPluginMainClassNameIsUsed.gradle @@ -0,0 +1,11 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'war' +apply plugin: 'org.springframework.boot' +apply plugin: 'application' + +mainClassName = 'com.example.CustomMain' diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle new file mode 100644 index 00000000000..96047897b24 --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginJvmArgumentsAreUsed.gradle @@ -0,0 +1,14 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'application' +apply plugin: 'org.springframework.boot' + +applicationDefaultJvmArgs = ['-Dcom.foo=bar', '-Dcom.bar=baz'] + +task echoJvmArguments { + println 'JVM arguments = ' + bootRun.jvmArgs +} diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginMainClassNameIsUsed.gradle b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginMainClassNameIsUsed.gradle new file mode 100644 index 00000000000..a9163cd7d3e --- /dev/null +++ b/spring-boot-tools/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/run/BootRunIntegrationTests-applicationPluginMainClassNameIsUsed.gradle @@ -0,0 +1,14 @@ +buildscript { + dependencies { + classpath files(pluginClasspath.split(',')) + } +} + +apply plugin: 'application' +apply plugin: 'org.springframework.boot' + +mainClassName = 'com.example.CustomMainClass' + +task echoMainClassName { + println 'Main class name = ' + bootRun.main +}