Merge branch '2.2.x'

Closes gh-18957
pull/18962/head
Andy Wilkinson 5 years ago
commit b9f4a9c075

@ -41,7 +41,7 @@ Explicit build support is provided for the following build tools:
| 3.3+
| Gradle
| 5.x (4.10 is also supported but in a deprecated form)
| 5.x and 6.x (4.10 is also supported but in a deprecated form)
|===
@ -195,7 +195,7 @@ In those cases, see <<using-spring-boot.adoc#using-boot-maven-without-a-parent>>
[[getting-started-gradle-installation]]
==== Gradle Installation
Spring Boot is compatible with 5.x.
Spring Boot is compatible with 5.x and 6.x.
4.10 is also supported but this support is deprecated and will be removed in a future release.
If you do not already have Gradle installed, you can follow the instructions at https://gradle.org.

@ -38,7 +38,7 @@ Andy Wilkinson
The Spring Boot Gradle Plugin provides Spring Boot support in https://gradle.org[Gradle].
It allows you to package executable jar or war archives, run Spring Boot applications, and use the dependency management provided by `spring-boot-dependencies`.
Spring Boot's Gradle plugin requires Gradle 5.x (4.10 is also supported but this support is deprecated and will be removed in a future release).
Spring Boot's Gradle plugin requires Gradle 5.x or 6.x (4.10 is also supported but this support is deprecated and will be removed in a future release).
In addition to this user guide, {api-documentation}[API documentation] is also available.

@ -49,4 +49,4 @@ include::../gradle/publishing/maven-publish.gradle.kts[tags=publishing]
When the {application-plugin}[`application` plugin] is applied a distribution named `boot` is created.
This distribution contains the archive produced by the `bootJar` or `bootWar` task and scripts to launch it on Unix-like platforms and Windows.
Zip and tar distributions can be built by the `bootDistZip` and `bootDistTar` tasks respectively.
To use the `application` plugin, its `mainClassName` project property must be configured with the name of your application's main class.
To use the `application` plugin, its `mainClassName` property must be configured with the name of your application's main class.

@ -60,7 +60,7 @@ include::../gradle/running/boot-run-disable-optimized-launch.gradle.kts[tags=lau
----
If the {application-plugin}[`application` plugin] has been applied, its `mainClassName` project property must be configured and can be used for the same purpose:
If the {application-plugin}[`application` plugin] has been applied, its `mainClassName` property must be configured and can be used for the same purpose:
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
.Groovy

@ -5,7 +5,9 @@ plugins {
}
// tag::main-class[]
mainClassName = 'com.example.ExampleApplication'
application {
mainClassName = 'com.example.ExampleApplication'
}
// end::main-class[]
task configuredMainClass {

@ -17,6 +17,7 @@
package org.springframework.boot.gradle.dsl;
import java.io.File;
import java.lang.reflect.Method;
import org.gradle.api.Action;
import org.gradle.api.Project;
@ -24,6 +25,7 @@ import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
import org.gradle.jvm.tasks.Jar;
import org.springframework.boot.gradle.tasks.buildinfo.BuildInfo;
@ -115,7 +117,7 @@ public class SpringBootExtension {
private String determineArtifactBaseName() {
Jar artifactTask = findArtifactTask();
return (artifactTask != null) ? artifactTask.getBaseName() : null;
return (artifactTask != null) ? getArchiveBaseName(artifactTask) : null;
}
private Jar findArtifactTask() {
@ -126,4 +128,26 @@ public class SpringBootExtension {
return (Jar) this.project.getTasks().findByName("bootJar");
}
private static String getArchiveBaseName(AbstractArchiveTask task) {
try {
Method method = findMethod(task.getClass(), "getArchiveBaseName");
if (method != null) {
return (String) method.invoke(task);
}
}
catch (Exception ex) {
// Continue
}
return task.getBaseName();
}
private static Method findMethod(Class<?> type, String name) {
for (Method candidate : type.getMethods()) {
if (candidate.getName().equals(name)) {
return candidate;
}
}
return null;
}
}

@ -20,6 +20,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import org.gradle.api.GradleException;
@ -32,6 +33,8 @@ import org.gradle.api.file.FileCollection;
import org.gradle.api.internal.IConventionAware;
import org.gradle.api.plugins.ApplicationPlugin;
import org.gradle.api.plugins.ApplicationPluginConvention;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator;
import org.springframework.boot.gradle.tasks.application.CreateBootStartScripts;
@ -49,10 +52,7 @@ final class ApplicationPluginAction implements PluginApplicationAction {
.getPlugin(ApplicationPluginConvention.class);
DistributionContainer distributions = project.getExtensions().getByType(DistributionContainer.class);
Distribution distribution = distributions.create("boot");
if (distribution instanceof IConventionAware) {
((IConventionAware) distribution).getConventionMapping().map("baseName",
() -> applicationConvention.getApplicationName() + "-boot");
}
configureBaseNameConvention(project, applicationConvention, distribution);
CreateBootStartScripts bootStartScripts = project.getTasks().create("bootStartScripts",
CreateBootStartScripts.class);
bootStartScripts
@ -79,6 +79,37 @@ final class ApplicationPluginAction implements PluginApplicationAction {
distribution.getContents().with(binCopySpec);
}
@SuppressWarnings("unchecked")
private void configureBaseNameConvention(Project project, ApplicationPluginConvention applicationConvention,
Distribution distribution) {
Method getDistributionBaseName = findMethod(distribution.getClass(), "getDistributionBaseName");
if (getDistributionBaseName != null) {
try {
Property<String> distributionBaseName = (Property<String>) distribution.getClass()
.getMethod("getDistributionBaseName").invoke(distribution);
distributionBaseName.getClass().getMethod("convention", Provider.class).invoke(distributionBaseName,
project.provider(() -> applicationConvention.getApplicationName() + "-boot"));
return;
}
catch (Exception ex) {
// Continue
}
}
if (distribution instanceof IConventionAware) {
((IConventionAware) distribution).getConventionMapping().map("baseName",
() -> applicationConvention.getApplicationName() + "-boot");
}
}
private static Method findMethod(Class<?> type, String name) {
for (Method candidate : type.getMethods()) {
if (candidate.getName().equals(name)) {
return candidate;
}
}
return null;
}
@Override
public Class<? extends Plugin<Project>> getPluginClass() {
return ApplicationPlugin.class;

@ -25,6 +25,7 @@ import java.util.function.Supplier;
import org.gradle.api.InvalidUserDataException;
import org.gradle.api.Project;
import org.gradle.api.file.FileCollection;
import org.gradle.api.plugins.JavaApplication;
import org.springframework.boot.gradle.dsl.SpringBootExtension;
import org.springframework.boot.loader.tools.MainClassFinder;
@ -53,11 +54,9 @@ final class MainClassConvention implements Callable<Object> {
if (springBootExtension != null && springBootExtension.getMainClassName() != null) {
return springBootExtension.getMainClassName();
}
if (this.project.hasProperty("mainClassName")) {
Object mainClassName = this.project.property("mainClassName");
if (mainClassName != null) {
return mainClassName;
}
JavaApplication javaApplication = this.project.getConvention().findByType(JavaApplication.class);
if (javaApplication != null && javaApplication.getMainClassName() != null) {
return javaApplication.getMainClassName();
}
return resolveMainClass();
}

@ -20,6 +20,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
@ -37,6 +38,7 @@ import org.gradle.api.java.archives.Attributes;
import org.gradle.api.specs.Spec;
import org.gradle.api.specs.Specs;
import org.gradle.api.tasks.WorkResult;
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
import org.gradle.api.tasks.bundling.Jar;
import org.gradle.api.tasks.util.PatternSet;
@ -93,7 +95,7 @@ class BootArchiveSupport {
}
CopyAction createCopyAction(Jar jar) {
CopyAction copyAction = new BootZipCopyAction(jar.getArchivePath(), jar.isPreserveFileTimestamps(),
CopyAction copyAction = new BootZipCopyAction(getOutputLocation(jar), jar.isPreserveFileTimestamps(),
isUsingDefaultLoader(jar), this.requiresUnpack.getAsSpec(), this.exclusions.getAsExcludeSpec(),
this.launchScript, this.compressionResolver, jar.getMetadataCharset());
if (!jar.isReproducibleFileOrder()) {
@ -102,6 +104,28 @@ class BootArchiveSupport {
return new ReproducibleOrderingCopyAction(copyAction);
}
private static File getOutputLocation(AbstractArchiveTask task) {
try {
Method method = findMethod(task.getClass(), "getArchiveFile");
if (method != null) {
return (File) method.invoke(task);
}
}
catch (Exception ex) {
// Continue
}
return task.getArchivePath();
}
private static Method findMethod(Class<?> type, String name) {
for (Method candidate : type.getMethods()) {
if (candidate.getName().equals(name)) {
return candidate;
}
}
return null;
}
private boolean isUsingDefaultLoader(Jar jar) {
return DEFAULT_LAUNCHER_CLASSES.contains(jar.getManifest().getAttributes().get("Main-Class"));
}

@ -19,6 +19,7 @@ package org.springframework.boot.gradle.tasks.bundling;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
@ -50,11 +51,32 @@ public class LaunchScriptConfiguration implements Serializable {
LaunchScriptConfiguration(AbstractArchiveTask archiveTask) {
Project project = archiveTask.getProject();
putIfMissing(this.properties, "initInfoProvides", archiveTask.getBaseName());
putIfMissing(this.properties, "initInfoShortDescription", removeLineBreaks(project.getDescription()),
archiveTask.getBaseName());
putIfMissing(this.properties, "initInfoDescription", augmentLineBreaks(project.getDescription()),
archiveTask.getBaseName());
String baseName = getArchiveBaseName(archiveTask);
putIfMissing(this.properties, "initInfoProvides", baseName);
putIfMissing(this.properties, "initInfoShortDescription", removeLineBreaks(project.getDescription()), baseName);
putIfMissing(this.properties, "initInfoDescription", augmentLineBreaks(project.getDescription()), baseName);
}
private static String getArchiveBaseName(AbstractArchiveTask task) {
try {
Method method = findMethod(task.getClass(), "getArchiveBaseName");
if (method != null) {
return (String) method.invoke(task);
}
}
catch (Exception ex) {
// Continue
}
return task.getBaseName();
}
private static Method findMethod(Class<?> type, String name) {
for (Method candidate : type.getMethods()) {
if (candidate.getName().equals(name)) {
return candidate;
}
}
return null;
}
/**

@ -39,7 +39,7 @@ import org.springframework.boot.gradle.testkit.GradleBuildExtension;
public final class GradleCompatibilityExtension implements TestTemplateInvocationContextProvider {
private static final List<String> GRADLE_VERSIONS = Arrays.asList("default", "5.0", "5.1.1", "5.2.1", "5.3.1",
"5.4.1", "5.5.1", "5.6.4");
"5.4.1", "5.5.1", "5.6.4", "6.0");
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {

@ -20,7 +20,8 @@ import java.io.File;
import java.io.IOException;
import org.gradle.api.Project;
import org.gradle.api.plugins.ExtraPropertiesExtension;
import org.gradle.api.plugins.ApplicationPlugin;
import org.gradle.api.plugins.JavaApplication;
import org.gradle.testfixtures.ProjectBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -51,9 +52,10 @@ class MainClassConventionTests {
}
@Test
void mainClassNameProjectPropertyIsUsed() throws Exception {
this.project.getExtensions().getByType(ExtraPropertiesExtension.class).set("mainClassName",
"com.example.MainClass");
void javaApplicationExtensionMainClassNameIsUsed() throws Exception {
this.project.getPlugins().apply(ApplicationPlugin.class);
JavaApplication extension = this.project.getExtensions().findByType(JavaApplication.class);
extension.setMainClassName("com.example.MainClass");
assertThat(this.convention.call()).isEqualTo("com.example.MainClass");
}
@ -67,8 +69,9 @@ class MainClassConventionTests {
@Test
void springBootExtensionMainClassNameIsUsedInPreferenceToMainClassNameProjectProperty() throws Exception {
this.project.getExtensions().getByType(ExtraPropertiesExtension.class).set("mainClassName",
"com.example.ProjectPropertyMainClass");
this.project.getPlugins().apply(ApplicationPlugin.class);
JavaApplication javaApplication = this.project.getExtensions().findByType(JavaApplication.class);
javaApplication.setMainClassName("com.example.JavaApplicationMainClass");
SpringBootExtension extension = this.project.getExtensions().create("springBoot", SpringBootExtension.class,
this.project);
extension.setMainClassName("com.example.SpringBootExtensionMainClass");

Loading…
Cancel
Save