diff --git a/.bomr/bomr.yaml b/.bomr/bomr.yaml
deleted file mode 100644
index a9ac945f30f..00000000000
--- a/.bomr/bomr.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-bomr:
- bom: spring-boot-project/spring-boot-dependencies/pom.xml
- upgrade:
- github:
- organization: spring-projects
- repository: spring-boot
- issue-labels:
- - 'type: dependency-upgrade'
- policy: same-major-version
- prohibited:
- - project: derby
- versions:
- # 10.15 requires Java 9
- - '[10.15,)'
- verify:
- ignored-dependencies:
- # Avoid conflicting transitive requirements for
- # io.grpc:grpc-core:jar:[1.0.1,1.0.1] (Jetty),
- # io.grpc:grpc-core:jar:[1.14.0,1.14.0] (Micrometer's Azure Registry), and
- # io.grpc:grpc-core:jar:[1.15.0,1.15.0] (Micrometer's Stackdriver Registry)
- - 'io.micrometer:micrometer-registry-azure-monitor'
- - 'org.eclipse.jetty.gcloud:jetty-gcloud-session-manager'
- - 'org.eclipse.jetty:jetty-home'
- repositories:
- # Caffeine Simulator's dependencies
- - 'https://maven.imagej.net/content/repositories/public/'
- # Spring Data GemFire's GemFire dependencies
- - 'https://repo.spring.io/gemstone-release-pivotal-cache'
diff --git a/.gitignore b/.gitignore
index 4dbf2657a90..d37eb49a80b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,12 +22,16 @@ MANIFEST.MF
_site/
activemq-data
bin
+build
+!/**/src/**/bin
+!/**/src/**/build
build.log
dependency-reduced-pom.xml
dump.rdb
interpolated*.xml
lib/
manifest.yml
+out
overridedb.*
target
transaction-logs
diff --git a/.mvn/jvm.config b/.mvn/jvm.config
deleted file mode 100644
index f432c960223..00000000000
--- a/.mvn/jvm.config
+++ /dev/null
@@ -1 +0,0 @@
--Xmx1536m
\ No newline at end of file
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
deleted file mode 100755
index e89f07c229c..00000000000
Binary files a/.mvn/wrapper/maven-wrapper.jar and /dev/null differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
deleted file mode 100755
index a84b7ef2d6a..00000000000
--- a/.mvn/wrapper/maven-wrapper.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip
-wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.3/maven-wrapper-0.5.3.jar
diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc
index 6c3c7c551b1..04a4ef94b70 100755
--- a/CONTRIBUTING.adoc
+++ b/CONTRIBUTING.adoc
@@ -49,11 +49,13 @@ added after the original pull request but before a merge.
* We use the https://github.com/spring-io/spring-javaformat/[Spring JavaFormat] project
to apply code formatting conventions. If you use Eclipse and you follow the '`Importing
into eclipse`' instructions below you should get project specific formatting
- automatically. You can also install the https://github.com/spring-io/spring-javaformat/#intellij-idea[Spring JavaFormat IntelliJ Plugin]
- or format the code from the Maven build by running
- `./mvnw io.spring.javaformat:spring-javaformat-maven-plugin:apply`.
+ automatically. You can also install the
+ https://github.com/spring-io/spring-javaformat/#intellij-idea[Spring JavaFormat IntelliJ
+ Plugin] or format the code from the Gradle build by running
+ `./gradlew format`.
* The build includes checkstyle rules for many of our code conventions. Run
- `./mvnw validate` if you want to check you changes are compliant.
+ `./gradlew checkstyleMain checkstyleTest` if you want to check you changes are
+ compliant.
* Make sure all new `.java` files to have a simple Javadoc class comment with at least an
`@author` tag identifying you, and preferably at least a paragraph on what the class is
for.
@@ -75,65 +77,24 @@ added after the original pull request but before a merge.
If you don't have an IDE preference we would recommend that you use
https://spring.io/tools/sts[Spring Tools Suite] or
https://eclipse.org[Eclipse] when working with the code. We use the
-https://eclipse.org/m2e/[M2Eclipse] eclipse plugin for maven support. Other IDEs and tools
-should also work without issue.
+https://projects.eclipse.org/projects/tools.buildship[Buildship] Eclipse plugin for Gradle
+support. Other IDEs and tools should also work without issue.
=== Building from Source
-Spring Boot source can be built from the command line using
-https://maven.apache.org/run-maven/index.html[Apache Maven] on JDK 1.8 or above. We
-include '`Maven Wrapper`' scripts (`./mvnw` or `mvnw.bat`) that you can run rather than
-needing to install Maven locally.
+Spring Boot source can be built from the command line using https://gradle.org[Gradle] on
+JDK 1.8 or above. We include https://docs.gradle.org/current/userguide/gradle_wrapper.html[Gradle's
+wrapper scripts] (`./gradlew` or `gradlew.bat`) that you can run rather than needing to
+install Gradle locally.
-
-
-==== Default Build
-The project can be built from the root directory using the standard Maven command:
+The project can be built from the root directory using the standard Gradle command:
[indent=0]
----
- $ ./mvnw clean install
+ $ ./gradlew build
----
-NOTE: You may need to increase the amount of memory available to Maven by setting
-a `MAVEN_OPTS` environment variable with the value `-Xmx512m`
-
-If you are rebuilding often, you might also want to skip the tests and the execution of
-checkstyle until you are ready to submit a pull request:
-
-[indent=0]
-----
- $ ./mvnw clean install -DskipTests -Pfast
-----
-
-
-
-==== Full Build
-You can run a full build using the following command:
-
-[indent=0]
-----
- $ ./mvnw -Pfull clean install
-----
-
-NOTE: As for the standard build, you may need to increase the amount of memory available
-to Maven by setting a `MAVEN_OPTS` environment variable with the value `-Xmx512m`. We
-generate more artifacts when running the full build (such as Javadoc jars), so you may
-find the process a little slower than the standard build.
-
-[TIP]
-====
-If you want to run a build without the smoke tests and integration tests, building the
-`spring-boot-project` module is enough. You can cd there and run the same command, or you
-can run this from the top-level directory:
-
-[indent=0]
-----
- $ ./mvnw -f spring-boot-project -Pfull clean install
-----
-====
-
=== Importing into Eclipse
@@ -141,9 +102,6 @@ You can import the Spring Boot code into any Eclipse 2019-09-based distribution.
easiest way to setup a new environment is to use the Eclipse Installer with the provided
`spring-boot-project.setup` file (in the `/eclipse` folder).
-NOTE: Due to m2e issue https://bugs.eclipse.org/bugs/show_bug.cgi?id=548652[#548652] you need to be running m2e 1.14.0 or higher.
-An early milestone is available from https://download.eclipse.org/technology/m2e/milestones/1.14/.
-
==== Using the Eclipse Installer
@@ -169,9 +127,6 @@ Once complete you should find that a local workspace has been provisioned comple
all required Eclipse plugins. Projects will be grouped into working-sets to make the code
easier to navigate.
-If you want to work on the `spring-boot-gradle-plugin` you should remove the imported Maven
-project and reimport it as a Gradle project.
-
TIP: If you see import errors with `com.sun` packages make sure you have setup a valid
`JavaSE-1.8` environment. From preferences select "`Java`", "`Installed JREs`",
"`Execution Environments`" and make sure "`JavaSE-1.8`" points to a Java 1.8
@@ -179,13 +134,13 @@ install (we use AdoptOpenJDK on our CI).
-==== Manual Installation with M2Eclipse
+==== Manual Installation with Buildship
If you prefer to install Eclipse yourself you should use the
-https://eclipse.org/m2e/[M2Eclipse] eclipse plugin. If you don't already have m2eclipse
-installed it is available from the "`Eclipse marketplace`".
+https://projects.eclipse.org/projects/tools.buildship[Buildship] Eclipse plugin. If you
+don't already have Buildship installed it is available from the "`Eclipse marketplace`".
Spring Boot includes project specific source formatting settings, in order to have these
-work with m2eclipse, we provide an additional Eclipse plugin that you can install:
+work with Buildship, we provide an additional Eclipse plugin that you can install:
@@ -197,21 +152,13 @@ work with m2eclipse, we provide an additional Eclipse plugin that you can instal
NOTE: The plugin is optional. Projects can be imported without the plugins, your code
changes just won't be automatically formatted.
-With the requisite eclipse plugins installed you can select
-`import existing maven projects` from the `file` menu to import the code. You will
-need to import the root `spring-boot` pom and the `spring-boot-smoke-tests` pom separately.
+With the requisite Eclipse plugins installed you can select
+`Gradle -> Existing Gradle project` from the `File -> Import…` menu to import the code.
=== Importing into IntelliJ IDEA
-**Please, do this first!**
-Go to `Preferences | Build, Execution, Deployment | Build Tools | Maven | Importing`
-and set `VM options for importer` to `-Xmx2g` to allocate sufficient memory for IDEA's
-Maven import process to parse the Spring Boot project structure. _Not doing so could
-mean the import fails silently, leaving the project setup incomplete._
-
-For the actual import use "`File`" -> "`Open`" and select the root `pom.xml`, or the
-`spring-boot-project/pom.xml` if you only want the Spring Boot project sources.
+Use "`File`" -> "`Open`" and then select the root `build.gradle` file to import the code.
@@ -239,18 +186,10 @@ needs to be added.
=== Importing into Other IDEs
-Maven is well supported by most Java IDEs. Refer to your vendor documentation.
+Gradle is well supported by most Java IDEs. Refer to your vendor documentation.
-== Integration Tests
-The smoke tests run as part of the build when you `./mvnw install`.
-Due to the fact that they make use of the `spring-boot-maven-plugin`
-they cannot be called directly, and so instead are launched via the
-`maven-invoker-plugin`. If you encounter build failures running the integration tests,
-check the `build.log` file in the appropriate smoke test directory.
-
-
== Cloning the git repository on Windows
Some files in the git repository may exceed the Windows maximum file path (260
characters), depending on where you clone the repository. If you get `Filename too long`
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 00000000000..bc35a69d006
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,20 @@
+plugins {
+ id 'org.jetbrains.kotlin.jvm' apply false // https://youtrack.jetbrains.com/issue/KT-30276
+}
+
+description = 'Spring Boot Build'
+
+allprojects {
+ group 'org.springframework.boot'
+
+ repositories {
+ mavenCentral()
+ maven { url 'https://repo.spring.io/milestone' }
+ maven { url 'https://repo.spring.io/snapshot' }
+ }
+
+ configurations.all {
+ resolutionStrategy.cacheChangingModulesFor 60, 'minutes'
+ }
+
+}
diff --git a/buildSrc/README.adoc b/buildSrc/README.adoc
new file mode 100644
index 00000000000..44c7bc7f432
--- /dev/null
+++ b/buildSrc/README.adoc
@@ -0,0 +1,26 @@
+= BOM Plugin
+
+Allows one to publish a BOM from Gradle.
+Properties, dependencies, and other BOMs are applied to the `bom {}` extension.
+
+This plugin applies the `java-platform` and `maven-publish` plugins to the Project it's applied to.
+
+== Usage
+
+[source,groovy,indent=0]
+----
+plugins {
+ id 'org.springframework.boot.bom'
+}
+
+bom {
+ property 'logback.version', '1.2.3'
+ property 'junit-jupiter.version', '5.3.2'
+
+ dependency 'ch.qos.logback', 'logback-classic', '${logback.version}'
+ dependency 'ch.qos.logback', 'logback-core', '${logback.version}'
+
+ bomImport 'org.junit', 'junit-bom', '${junit-jupiter.version}'
+}
+----
+
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
new file mode 100644
index 00000000000..b0587d4d33e
--- /dev/null
+++ b/buildSrc/build.gradle
@@ -0,0 +1,84 @@
+plugins {
+ id 'java-gradle-plugin'
+ id 'io.spring.javaformat' version '0.0.18-SNAPSHOT'
+ id 'checkstyle'
+}
+
+repositories {
+ mavenCentral()
+ gradlePluginPortal()
+ maven { url 'https://repo.spring.io/release' }
+}
+
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
+
+dependencies {
+ checkstyle 'io.spring.javaformat:spring-javaformat-checkstyle:0.0.15'
+ implementation 'com.fasterxml.jackson.core:jackson-databind:2.10.0'
+ implementation 'commons-codec:commons-codec:1.13'
+ implementation 'org.apache.maven:maven-embedder:3.6.2'
+ implementation 'org.asciidoctor:asciidoctor-gradle-jvm:2.4.0'
+ implementation 'org.springframework:spring-core:5.2.2.RELEASE'
+ implementation 'org.springframework:spring-web:5.2.2.RELEASE'
+ implementation 'com.google.code.gson:gson:2.8.5'
+ implementation 'io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.15'
+ testImplementation 'org.assertj:assertj-core:3.11.1'
+ testImplementation 'org.apache.logging.log4j:log4j-core:2.12.1'
+ testImplementation 'org.junit.jupiter:junit-jupiter:5.5.2'
+}
+
+checkstyle {
+ def archive = configurations.checkstyle.filter { it.name.startsWith('spring-javaformat-checkstyle')}
+ config = resources.text.fromArchiveEntry(archive, 'io/spring/javaformat/checkstyle/checkstyle.xml')
+ toolVersion = 8.11
+}
+
+gradlePlugin {
+ plugins {
+ autoConfigurationPlugin {
+ id = "org.springframework.boot.auto-configuration"
+ implementationClass = "org.springframework.boot.build.autoconfigure.AutoConfigurationPlugin"
+ }
+ bomPlugin {
+ id = "org.springframework.boot.bom"
+ implementationClass = "org.springframework.boot.build.bom.BomPlugin"
+ }
+ configurationPropertiesPlugin {
+ id = "org.springframework.boot.configuration-properties"
+ implementationClass = "org.springframework.boot.build.context.properties.ConfigurationPropertiesPlugin"
+ }
+ conventionsPlugin {
+ id = "org.springframework.boot.conventions"
+ implementationClass = "org.springframework.boot.build.ConventionsPlugin"
+ }
+ deployedPlugin {
+ id = "org.springframework.boot.deployed"
+ implementationClass = "org.springframework.boot.build.DeployedPlugin"
+ }
+ integrationTestPlugin {
+ id = "org.springframework.boot.integration-test"
+ implementationClass = "org.springframework.boot.build.test.IntegrationTestPlugin"
+ }
+ mavenPluginPlugin {
+ id = "org.springframework.boot.maven-plugin"
+ implementationClass = "org.springframework.boot.build.mavenplugin.MavenPluginPlugin"
+ }
+ mavenRepositoryPlugin {
+ id = "org.springframework.boot.maven-repository"
+ implementationClass = "org.springframework.boot.build.MavenRepositoryPlugin"
+ }
+ optionalDependenciesPlugin {
+ id = "org.springframework.boot.optional-dependencies"
+ implementationClass = "org.springframework.boot.build.optional.OptionalDependenciesPlugin"
+ }
+ starterPlugin {
+ id = "org.springframework.boot.starter"
+ implementationClass = "org.springframework.boot.build.starters.StarterPlugin"
+ }
+ }
+}
+
+test {
+ useJUnitPlatform()
+}
diff --git a/buildSrc/settings.gradle b/buildSrc/settings.gradle
new file mode 100644
index 00000000000..ce6d3a43dae
--- /dev/null
+++ b/buildSrc/settings.gradle
@@ -0,0 +1,16 @@
+pluginManagement {
+ repositories {
+ mavenCentral()
+ gradlePluginPortal()
+ maven { url 'https://repo.spring.io/snapshot' }
+ }
+ resolutionStrategy {
+ eachPlugin {
+ if (requested.id.id == 'io.spring.javaformat') {
+ useModule "io.spring.javaformat:spring-javaformat-gradle-plugin:${requested.version}"
+ }
+ }
+ }
+}
+
+apply from: new File(settingsDir, '../gradle/build-cache-settings.gradle')
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/AsciidoctorConventions.java b/buildSrc/src/main/java/org/springframework/boot/build/AsciidoctorConventions.java
new file mode 100644
index 00000000000..132940f6855
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/AsciidoctorConventions.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 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 org.springframework.boot.build;
+
+import java.io.File;
+import java.net.URI;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask;
+import org.asciidoctor.gradle.jvm.AsciidoctorJExtension;
+import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin;
+import org.asciidoctor.gradle.jvm.AsciidoctorTask;
+import org.gradle.api.Action;
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.Sync;
+import org.gradle.api.tasks.TaskAction;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * Conventions that are applied in the presence of the {@link AsciidoctorJPlugin}. When
+ * the plugin is applied:
+ *
+ *
+ * All warnings are made fatal.
+ * A task is created to resolve and unzip our documentation resources (CSS and
+ * Javascript).
+ * For each {@link AsciidoctorTask} (HTML only):
+ *
+ * A task is created to sync the documentation resources to its output directory.
+ * {@code doctype} {@link AsciidoctorTask#options(Map) option} is configured.
+ * {@link AsciidoctorTask#attributes(Map) Attributes} are configured for syntax
+ * highlighting, CSS styling, docinfo, etc.
+ *
+ * For each {@link AbstractAsciidoctorTask} (HTML and PDF):
+ *
+ * {@link AsciidoctorTask#attributes(Map) Attributes} are configured to enable
+ * warnings for references to missing attributes, the GitHub tag, the Artifactory repo for
+ * the current version, etc.
+ * {@link AbstractAsciidoctorTask#baseDirFollowsSourceDir() baseDirFollowsSourceDir()}
+ * is enabled.
+ *
+ *
+ *
+ * @author Andy Wilkinson
+ */
+class AsciidoctorConventions {
+
+ void apply(Project project) {
+ project.getPlugins().withType(AsciidoctorJPlugin.class, (asciidoctorPlugin) -> {
+ configureDocResourcesRepository(project);
+ makeAllWarningsFatal(project);
+ UnzipDocumentationResources unzipResources = createUnzipDocumentationResourcesTask(project);
+ project.getTasks().withType(AbstractAsciidoctorTask.class, (asciidoctorTask) -> {
+ configureCommonAttributes(project, asciidoctorTask);
+ configureOptions(asciidoctorTask);
+ asciidoctorTask.baseDirFollowsSourceDir();
+ Sync syncSource = createSyncDocumentationSourceTask(project, asciidoctorTask);
+ if (asciidoctorTask instanceof AsciidoctorTask) {
+ configureHtmlOnlyAttributes(project, asciidoctorTask);
+ syncSource.from(unzipResources, (resources) -> resources.into("asciidoc"));
+ asciidoctorTask.doFirst(new Action() {
+
+ @Override
+ public void execute(Task task) {
+ project.copy((spec) -> {
+ spec.from(asciidoctorTask.getSourceDir());
+ spec.into(asciidoctorTask.getOutputDir());
+ spec.include("css/**", "js/**");
+ });
+ }
+
+ });
+ }
+ });
+ });
+ }
+
+ private void configureDocResourcesRepository(Project project) {
+ project.getRepositories().maven((mavenRepo) -> {
+ mavenRepo.setUrl(URI.create("https://repo.spring.io/release"));
+ mavenRepo.mavenContent((mavenContent) -> mavenContent.includeGroup("io.spring.docresources"));
+ });
+ }
+
+ private void makeAllWarningsFatal(Project project) {
+ project.getExtensions().getByType(AsciidoctorJExtension.class).fatalWarnings(".*");
+ }
+
+ private UnzipDocumentationResources createUnzipDocumentationResourcesTask(Project project) {
+ Configuration documentationResources = project.getConfigurations().maybeCreate("documentationResources");
+ documentationResources.getDependencies()
+ .add(project.getDependencies().create("io.spring.docresources:spring-doc-resources:0.1.3.RELEASE"));
+ UnzipDocumentationResources unzipResources = project.getTasks().create("unzipDocumentationResources",
+ UnzipDocumentationResources.class);
+ unzipResources.setResources(documentationResources);
+ unzipResources.setOutputDir(new File(project.getBuildDir(), "docs/resources"));
+ return unzipResources;
+ }
+
+ private Sync createSyncDocumentationSourceTask(Project project, AbstractAsciidoctorTask asciidoctorTask) {
+ Sync syncDocumentationSource = project.getTasks()
+ .create("syncDocumentationSourceFor" + StringUtils.capitalize(asciidoctorTask.getName()), Sync.class);
+ File syncedSource = new File(project.getBuildDir(), "docs/src/" + asciidoctorTask.getName());
+ syncDocumentationSource.setDestinationDir(syncedSource);
+ syncDocumentationSource.from("src/docs/");
+ asciidoctorTask.dependsOn(syncDocumentationSource);
+ asciidoctorTask.setSourceDir(project.relativePath(new File(syncedSource, "asciidoc/")));
+ return syncDocumentationSource;
+ }
+
+ private void configureOptions(AbstractAsciidoctorTask asciidoctorTask) {
+ asciidoctorTask.options(Collections.singletonMap("doctype", "book"));
+ }
+
+ private void configureHtmlOnlyAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) {
+ Map attributes = new HashMap<>();
+ attributes.put("highlightjsdir", "js/highlight");
+ attributes.put("highlightjs-theme", "github");
+ attributes.put("linkcss", true);
+ attributes.put("icons", "font");
+ attributes.put("stylesheet", "css/spring.css");
+ asciidoctorTask.attributes(attributes);
+ }
+
+ private void configureCommonAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) {
+ Map attributes = new HashMap<>();
+ attributes.put("attribute-missing", "warn");
+ attributes.put("github-tag", determineGitHubTag(project));
+ attributes.put("spring-boot-artifactory-repo", determineArtifactoryRepo(project));
+ attributes.put("version", "{gradle-project-version}");
+ asciidoctorTask.attributes(attributes);
+ }
+
+ private String determineArtifactoryRepo(Project project) {
+ String version = project.getVersion().toString();
+ String type = version.substring(version.lastIndexOf('.'));
+ if (type.equals("RELEASE")) {
+ return "release";
+ }
+ if (type.startsWith("M") || type.startsWith("RC")) {
+ return "milestone";
+ }
+ return "snapshot";
+ }
+
+ private String determineGitHubTag(Project project) {
+ String version = "v" + project.getVersion();
+ return (version.endsWith("-SNAPSHOT")) ? "master" : version;
+ }
+
+ /**
+ * {@link Task} for unzipping the documentation resources.
+ */
+ public static class UnzipDocumentationResources extends DefaultTask {
+
+ private FileCollection resources;
+
+ private File outputDir;
+
+ @InputFiles
+ public FileCollection getResources() {
+ return this.resources;
+ }
+
+ public void setResources(FileCollection resources) {
+ this.resources = resources;
+ }
+
+ @OutputDirectory
+ public File getOutputDir() {
+ return this.outputDir;
+ }
+
+ public void setOutputDir(File outputDir) {
+ this.outputDir = outputDir;
+ }
+
+ @TaskAction
+ void syncDocumentationResources() {
+ getProject().sync((copySpec) -> {
+ copySpec.into(this.outputDir);
+ for (File resource : this.resources) {
+ copySpec.from(getProject().zipTree(resource));
+ }
+ });
+ }
+
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/ConventionsPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/ConventionsPlugin.java
new file mode 100644
index 00000000000..30aa02b7267
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/ConventionsPlugin.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import io.spring.javaformat.gradle.SpringJavaFormatPlugin;
+import org.apache.maven.artifact.repository.MavenArtifactRepository;
+import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.DependencySet;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.plugins.JavaPluginExtension;
+import org.gradle.api.plugins.quality.CheckstyleExtension;
+import org.gradle.api.plugins.quality.CheckstylePlugin;
+import org.gradle.api.publish.PublishingExtension;
+import org.gradle.api.publish.maven.MavenPom;
+import org.gradle.api.publish.maven.MavenPomDeveloperSpec;
+import org.gradle.api.publish.maven.MavenPomIssueManagement;
+import org.gradle.api.publish.maven.MavenPomLicenseSpec;
+import org.gradle.api.publish.maven.MavenPomOrganization;
+import org.gradle.api.publish.maven.MavenPomScm;
+import org.gradle.api.publish.maven.MavenPublication;
+import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
+import org.gradle.api.tasks.bundling.Jar;
+import org.gradle.api.tasks.compile.JavaCompile;
+import org.gradle.api.tasks.javadoc.Javadoc;
+import org.gradle.api.tasks.testing.Test;
+
+/**
+ * Plugin to apply conventions to projects that are part of Spring Boot's build.
+ * Conventions are applied in response to various plugins being applied.
+ *
+ *
+ *
+ * When the {@link JavaPlugin Java plugin} is applied:
+ *
+ *
+ * {@code sourceCompatibility} is set to {@code 1.8}
+ * Spring Java Format and Checkstyle plugins are applied
+ * {@link Test} tasks are configured to use JUnit Platform and use a max heap of 1024M
+ * {@link JavaCompile} tasks are configured to use UTF-8 encoding
+ * {@link Javadoc} tasks are configured to use UTF-8 encoding
+ * {@link Jar} tasks are configured to have the following manifest entries:
+ *
+ * {@code Automatic-Module-Name}
+ * {@code Build-Jdk-Spec}
+ * {@code Built-By}
+ * {@code Implementation-Title}
+ * {@code Implementation-Version}
+ *
+ *
+ *
+ *
+ *
+ * When the {@link MavenPublishPlugin Maven Publish plugin} is applied:
+ *
+ *
+ * If the {@code deploymentRepository} property has been set, a
+ * {@link MavenArtifactRepository Maven artifact repository} is configured to publish to
+ * it.
+ * The poms of all {@link MavenPublication Maven publications} are customized to meet
+ * Maven Central's requirements.
+ * If the {@link JavaPlugin Java plugin} has also been applied, creation of Javadoc
+ * and source jars is enabled.
+ *
+ *
+ *
+ *
+ * When the {@link AsciidoctorJPlugin} is applied, the conventions in
+ * {@link AsciidoctorConventions} are applied.
+ *
+ * @author Andy Wilkinson
+ */
+public class ConventionsPlugin implements Plugin {
+
+ @Override
+ public void apply(Project project) {
+ applyJavaConventions(project);
+ applyAsciidoctorConventions(project);
+ applyMavenPublishingConventions(project);
+ }
+
+ private void applyJavaConventions(Project project) {
+ project.getPlugins().withType(JavaPlugin.class, (java) -> {
+ configureSpringJavaFormat(project);
+ project.setProperty("sourceCompatibility", "1.8");
+ project.getTasks().withType(JavaCompile.class, (compile) -> compile.getOptions().setEncoding("UTF-8"));
+ project.getTasks().withType(Javadoc.class,
+ (javadoc) -> javadoc.getOptions().source("1.8").encoding("UTF-8"));
+ project.getTasks().withType(Test.class, (test) -> {
+ test.useJUnitPlatform();
+ test.setMaxHeapSize("1024M");
+ });
+ project.getTasks().withType(Jar.class, (jar) -> {
+ project.afterEvaluate((evaluated) -> {
+ jar.manifest((manifest) -> {
+ Map attributes = new TreeMap<>();
+ attributes.put("Automatic-Module-Name", project.getName().replace("-", "."));
+ attributes.put("Build-Jdk-Spec", project.property("sourceCompatibility"));
+ attributes.put("Built-By", "Spring");
+ attributes.put("Implementation-Title", project.getDescription());
+ attributes.put("Implementation-Version", project.getVersion());
+ manifest.attributes(attributes);
+ });
+ });
+ });
+ });
+ }
+
+ private void configureSpringJavaFormat(Project project) {
+ project.getPlugins().apply(SpringJavaFormatPlugin.class);
+ project.getPlugins().apply(CheckstylePlugin.class);
+ CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
+ checkstyle.setToolVersion("8.22");
+ checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
+ String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
+ DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
+ checkstyleDependencies
+ .add(project.getDependencies().create("io.spring.javaformat:spring-javaformat-checkstyle:" + version));
+ checkstyleDependencies
+ .add(project.getDependencies().create("io.spring.nohttp:nohttp-checkstyle:0.0.3.RELEASE"));
+ }
+
+ private void applyAsciidoctorConventions(Project project) {
+ new AsciidoctorConventions().apply(project);
+ }
+
+ private void applyMavenPublishingConventions(Project project) {
+ project.getPlugins().withType(MavenPublishPlugin.class).all((mavenPublish) -> {
+ PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
+ if (project.hasProperty("deploymentRepository")) {
+ publishing.getRepositories().maven((mavenRepository) -> {
+ mavenRepository.setUrl(project.property("deploymentRepository"));
+ mavenRepository.setName("deployment");
+ });
+ }
+ publishing.getPublications().withType(MavenPublication.class)
+ .all((mavenPublication) -> customizePom(mavenPublication.getPom(), project));
+ project.getPlugins().withType(JavaPlugin.class).all((javaPlugin) -> {
+ JavaPluginExtension extension = project.getExtensions().getByType(JavaPluginExtension.class);
+ extension.withJavadocJar();
+ extension.withSourcesJar();
+ });
+ });
+ }
+
+ private void customizePom(MavenPom pom, Project project) {
+ pom.getUrl().set("https://projects.spring.io/spring-boot/#");
+ pom.getDescription().set(project.provider(project::getDescription));
+ pom.organization(this::customizeOrganization);
+ pom.licenses(this::customizeLicences);
+ pom.developers(this::customizeDevelopers);
+ pom.scm(this::customizeScm);
+ pom.issueManagement(this::customizeIssueManagement);
+ }
+
+ private void customizeOrganization(MavenPomOrganization organization) {
+ organization.getName().set("Pivotal Software, Inc.");
+ organization.getUrl().set("https://spring.io");
+ }
+
+ private void customizeLicences(MavenPomLicenseSpec licences) {
+ licences.license((licence) -> {
+ licence.getName().set("Apache License, Version 2.0");
+ licence.getUrl().set("http://www.apache.org/licenses/LICENSE-2.0");
+ });
+ }
+
+ private void customizeDevelopers(MavenPomDeveloperSpec developers) {
+ developers.developer((developer) -> {
+ developer.getName().set("Pivotal");
+ developer.getEmail().set("info@pivotal.io");
+ developer.getOrganization().set("Pivotal Software, Inc.");
+ developer.getOrganizationUrl().set("https://www.spring.io");
+ });
+ }
+
+ private void customizeScm(MavenPomScm scm) {
+ scm.getConnection().set("scm:git:git://github.com/spring-projects/spring-boot.git");
+ scm.getDeveloperConnection().set("scm:git:ssh://git@github.com/spring-projects/spring-boot.git");
+ scm.getUrl().set("https://github.com/spring-projects/spring-boot");
+
+ }
+
+ private void customizeIssueManagement(MavenPomIssueManagement issueManagement) {
+ issueManagement.getSystem().set("GitHub");
+ issueManagement.getUrl().set("https://github.com/spring-projects/spring-boot/issues");
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/DeployedPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/DeployedPlugin.java
new file mode 100644
index 00000000000..30dc9d3db1c
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/DeployedPlugin.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 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 org.springframework.boot.build;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.plugins.JavaPlatformPlugin;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.publish.PublishingExtension;
+import org.gradle.api.publish.maven.MavenPublication;
+import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
+
+/**
+ * A plugin applied to a project that should be deployed.
+ *
+ * @author Andy Wilkinson
+ */
+public class DeployedPlugin implements Plugin {
+
+ /**
+ * Name of the task that generates the deployed pom file.
+ */
+ public static final String GENERATE_POM_TASK_NAME = "generatePomFileForMavenPublication";
+
+ @Override
+ public void apply(Project project) {
+ project.getPlugins().apply(MavenPublishPlugin.class);
+ project.getPlugins().apply(MavenRepositoryPlugin.class);
+ PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
+ MavenPublication mavenPublication = publishing.getPublications().create("maven", MavenPublication.class);
+ project.getPlugins().withType(JavaPlugin.class)
+ .all((javaPlugin) -> project.getComponents().matching((component) -> component.getName().equals("java"))
+ .all((javaComponent) -> mavenPublication.from(javaComponent)));
+ project.getPlugins().withType(JavaPlatformPlugin.class)
+ .all((javaPlugin) -> project.getComponents()
+ .matching((component) -> component.getName().equals("javaPlatform"))
+ .all((javaComponent) -> mavenPublication.from(javaComponent)));
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/MavenRepositoryPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/MavenRepositoryPlugin.java
new file mode 100644
index 00000000000..d7fcbfdcbc2
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/MavenRepositoryPlugin.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.gradle.api.Action;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.DependencySet;
+import org.gradle.api.artifacts.ProjectDependency;
+import org.gradle.api.plugins.JavaLibraryPlugin;
+import org.gradle.api.plugins.JavaPlatformPlugin;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.publish.PublishingExtension;
+import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
+
+/**
+ * A plugin to make a project's {@code deployment} publication available as a Maven
+ * repository. The repository can be consumed by depending upon the project using the
+ * {@code mavenRepository} configuration.
+ *
+ * @author Andy Wilkinson
+ */
+public class MavenRepositoryPlugin implements Plugin {
+
+ /**
+ * Name of the {@code mavenRepository} configuration.
+ */
+ public static final String MAVEN_REPOSITORY_CONFIGURATION_NAME = "mavenRepository";
+
+ /**
+ * Name of the task that publishes to the project repository.
+ */
+ public static final String PUBLISH_TO_PROJECT_REPOSITORY_TASK_NAME = "publishMavenPublicationToProjectRepository";
+
+ @Override
+ public void apply(Project project) {
+ project.getPlugins().apply(MavenPublishPlugin.class);
+ PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
+ File repositoryLocation = new File(project.getBuildDir(), "maven-repository");
+ publishing.getRepositories().maven((mavenRepository) -> {
+ mavenRepository.setName("project");
+ mavenRepository.setUrl(repositoryLocation.toURI());
+ });
+ project.getTasks().matching((task) -> task.getName().equals(PUBLISH_TO_PROJECT_REPOSITORY_TASK_NAME))
+ .all((task) -> setUpProjectRepository(project, task, repositoryLocation));
+ project.getTasks().matching((task) -> task.getName().equals("publishPluginMavenPublicationToProjectRepository"))
+ .all((task) -> setUpProjectRepository(project, task, repositoryLocation));
+ }
+
+ private void setUpProjectRepository(Project project, Task publishTask, File repositoryLocation) {
+ publishTask.doFirst(new CleanAction(repositoryLocation));
+ Configuration projectRepository = project.getConfigurations().create(MAVEN_REPOSITORY_CONFIGURATION_NAME);
+ project.getArtifacts().add(projectRepository.getName(), repositoryLocation,
+ (artifact) -> artifact.builtBy(publishTask));
+ DependencySet target = projectRepository.getDependencies();
+ project.getPlugins().withType(JavaPlugin.class).all((javaPlugin) -> addMavenRepositoryDependencies(project,
+ JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, target));
+ project.getPlugins().withType(JavaLibraryPlugin.class)
+ .all((javaLibraryPlugin) -> addMavenRepositoryDependencies(project, JavaPlugin.API_CONFIGURATION_NAME,
+ target));
+ project.getPlugins().withType(JavaPlatformPlugin.class)
+ .all((javaPlugin) -> addMavenRepositoryDependencies(project, JavaPlatformPlugin.API_CONFIGURATION_NAME,
+ target));
+ }
+
+ private void addMavenRepositoryDependencies(Project project, String sourceConfigurationName, DependencySet target) {
+ project.getConfigurations().getByName(sourceConfigurationName).getDependencies()
+ .withType(ProjectDependency.class).all((dependency) -> {
+ Map dependencyDescriptor = new HashMap<>();
+ dependencyDescriptor.put("path", dependency.getDependencyProject().getPath());
+ dependencyDescriptor.put("configuration", MAVEN_REPOSITORY_CONFIGURATION_NAME);
+ target.add(project.getDependencies().project(dependencyDescriptor));
+ });
+ }
+
+ private static final class CleanAction implements Action {
+
+ private final File location;
+
+ private CleanAction(File location) {
+ this.location = location;
+ }
+
+ @Override
+ public void execute(Task task) {
+ task.getProject().delete(this.location);
+ }
+
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/autoconfigure/AutoConfigurationMetadata.java b/buildSrc/src/main/java/org/springframework/boot/build/autoconfigure/AutoConfigurationMetadata.java
new file mode 100644
index 00000000000..c4664062b46
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/autoconfigure/AutoConfigurationMetadata.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.autoconfigure;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Properties;
+import java.util.concurrent.Callable;
+
+import org.gradle.api.Task;
+import org.gradle.api.internal.AbstractTask;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.tasks.TaskAction;
+
+/**
+ * A {@link Task} for generating metadata describing a project's auto-configuration
+ * classes.
+ *
+ * @author Andy Wilkinson
+ */
+public class AutoConfigurationMetadata extends AbstractTask {
+
+ private SourceSet sourceSet;
+
+ private File outputFile;
+
+ public AutoConfigurationMetadata() {
+ getInputs().file((Callable) () -> new File(this.sourceSet.getOutput().getResourcesDir(),
+ "META-INF/spring.factories"));
+ dependsOn((Callable) () -> this.sourceSet.getProcessResourcesTaskName());
+ getProject().getConfigurations()
+ .maybeCreate(AutoConfigurationPlugin.AUTO_CONFIGURATION_METADATA_CONFIGURATION_NAME);
+ }
+
+ public void setSourceSet(SourceSet sourceSet) {
+ this.sourceSet = sourceSet;
+ }
+
+ @OutputFile
+ public File getOutputFile() {
+ return this.outputFile;
+ }
+
+ public void setOutputFile(File outputFile) {
+ this.outputFile = outputFile;
+ }
+
+ @TaskAction
+ void documentAutoConfiguration() throws IOException {
+ Properties autoConfiguration = readAutoConfiguration();
+ getOutputFile().getParentFile().mkdirs();
+ try (FileWriter writer = new FileWriter(getOutputFile())) {
+ autoConfiguration.store(writer, null);
+ }
+ }
+
+ private Properties readAutoConfiguration() throws IOException {
+ Properties autoConfiguration = new Properties();
+ Properties springFactories = readSpringFactories(
+ new File(this.sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories"));
+ autoConfiguration.setProperty("autoConfigurationClassNames",
+ springFactories.getProperty("org.springframework.boot.autoconfigure.EnableAutoConfiguration"));
+ autoConfiguration.setProperty("module", getProject().getName());
+ return autoConfiguration;
+ }
+
+ private Properties readSpringFactories(File file) throws IOException {
+ Properties springFactories = new Properties();
+ try (Reader in = new FileReader(file)) {
+ springFactories.load(in);
+ }
+ return springFactories;
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/autoconfigure/AutoConfigurationPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/autoconfigure/AutoConfigurationPlugin.java
new file mode 100644
index 00000000000..7c2c77b4d21
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/autoconfigure/AutoConfigurationPlugin.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 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 org.springframework.boot.build.autoconfigure;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.concurrent.Callable;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.plugins.JavaPluginConvention;
+import org.gradle.api.tasks.SourceSet;
+
+import org.springframework.boot.build.DeployedPlugin;
+import org.springframework.boot.build.context.properties.ConfigurationPropertiesPlugin;
+
+/**
+ * {@link Plugin} for projects that define auto-configuration. When applied, the plugin
+ * applies the {@link DeployedPlugin}. Additionally, it reacts to the presence of the
+ * {@link JavaPlugin} by:
+ *
+ *
+ * Applying the {@link ConfigurationPropertiesPlugin}.
+ * Adding a dependency on the auto-configuration annotation processor.
+ * Defining a task that produces metadata describing the auto-configuration. The
+ * metadata is made available as an artifact in the
+ *
+ *
+ * @author Andy Wilkinson
+ */
+public class AutoConfigurationPlugin implements Plugin {
+
+ /**
+ * Name of the {@link Configuration} that holds the auto-configuration metadata
+ * artifact.
+ */
+ public static final String AUTO_CONFIGURATION_METADATA_CONFIGURATION_NAME = "autoConfigurationMetadata";
+
+ @Override
+ public void apply(Project project) {
+ project.getPlugins().apply(DeployedPlugin.class);
+ project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
+ project.getPlugins().apply(ConfigurationPropertiesPlugin.class);
+ Configuration annotationProcessors = project.getConfigurations()
+ .getByName(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME);
+ annotationProcessors.getDependencies()
+ .add(project.getDependencies().project(Collections.singletonMap("path",
+ ":spring-boot-project:spring-boot-tools:spring-boot-autoconfigure-processor")));
+ annotationProcessors.getDependencies()
+ .add(project.getDependencies().project(Collections.singletonMap("path",
+ ":spring-boot-project:spring-boot-tools:spring-boot-configuration-processor")));
+ project.getTasks().create("autoConfigurationMetadata", AutoConfigurationMetadata.class, (task) -> {
+ task.setSourceSet(project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets()
+ .getByName(SourceSet.MAIN_SOURCE_SET_NAME));
+ task.setOutputFile(new File(project.getBuildDir(), "auto-configuration-metadata.properties"));
+ project.getArtifacts().add(AutoConfigurationPlugin.AUTO_CONFIGURATION_METADATA_CONFIGURATION_NAME,
+ project.provider((Callable) task::getOutputFile), (artifact) -> artifact.builtBy(task));
+ });
+ });
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/autoconfigure/DocumentAutoConfigurationClasses.java b/buildSrc/src/main/java/org/springframework/boot/build/autoconfigure/DocumentAutoConfigurationClasses.java
new file mode 100644
index 00000000000..6302342dd75
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/autoconfigure/DocumentAutoConfigurationClasses.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.autoconfigure;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.util.Properties;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+import org.gradle.api.Task;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.AbstractTask;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link Task} used to document auto-configuration classes.
+ *
+ * @author Andy Wilkinson
+ */
+public class DocumentAutoConfigurationClasses extends AbstractTask {
+
+ private FileCollection autoConfiguration;
+
+ private File outputDir;
+
+ @InputFiles
+ public FileCollection getAutoConfiguration() {
+ return this.autoConfiguration;
+ }
+
+ public void setAutoConfiguration(FileCollection autoConfiguration) {
+ this.autoConfiguration = autoConfiguration;
+ }
+
+ @OutputDirectory
+ public File getOutputDir() {
+ return this.outputDir;
+ }
+
+ public void setOutputDir(File outputDir) {
+ this.outputDir = outputDir;
+ }
+
+ @TaskAction
+ void documentAutoConfigurationClasses() throws IOException {
+ for (File metadataFile : this.autoConfiguration) {
+ Properties metadata = new Properties();
+ try (Reader reader = new FileReader(metadataFile)) {
+ metadata.load(reader);
+ }
+ AutoConfiguration autoConfiguration = new AutoConfiguration(metadata.getProperty("module"), new TreeSet<>(
+ StringUtils.commaDelimitedListToSet(metadata.getProperty("autoConfigurationClassNames"))));
+ writeTable(autoConfiguration);
+ }
+ }
+
+ private void writeTable(AutoConfiguration autoConfigurationClasses) throws IOException {
+ this.outputDir.mkdirs();
+ try (PrintWriter writer = new PrintWriter(
+ new FileWriter(new File(this.outputDir, autoConfigurationClasses.module + ".adoc")))) {
+ writer.println("[cols=\"4,1\"]");
+ writer.println("|===");
+ writer.println("| Configuration Class | Links");
+
+ for (AutoConfigurationClass autoConfigurationClass : autoConfigurationClasses.classes) {
+ writer.println();
+ writer.printf("| {spring-boot-code}/spring-boot-project/%s/src/main/java/%s.java[`%s`]%n",
+ autoConfigurationClasses.module, autoConfigurationClass.path, autoConfigurationClass.name);
+ writer.printf("| {spring-boot-api}/%s.html[javadoc]%n", autoConfigurationClass.path);
+ }
+
+ writer.println("|===");
+ }
+ }
+
+ private static final class AutoConfiguration {
+
+ private final String module;
+
+ private final SortedSet classes;
+
+ private AutoConfiguration(String module, Set classNames) {
+ this.module = module;
+ this.classes = classNames.stream().map((className) -> {
+ String path = className.replace('.', '/');
+ String name = className.substring(className.lastIndexOf('.') + 1);
+ return new AutoConfigurationClass(name, path);
+ }).collect(Collectors.toCollection(TreeSet::new));
+ }
+
+ }
+
+ private static final class AutoConfigurationClass implements Comparable {
+
+ private final String name;
+
+ private final String path;
+
+ private AutoConfigurationClass(String name, String path) {
+ this.name = name;
+ this.path = path;
+ }
+
+ @Override
+ public int compareTo(AutoConfigurationClass other) {
+ return this.name.compareTo(other.name);
+ }
+
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/BomExtension.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/BomExtension.java
new file mode 100644
index 00000000000..e524749a0da
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/BomExtension.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyObjectSupport;
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.artifact.versioning.VersionRange;
+import org.gradle.api.InvalidUserCodeException;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.util.ConfigureUtil;
+
+import org.springframework.boot.build.bom.Library.Exclusion;
+import org.springframework.boot.build.bom.Library.Group;
+import org.springframework.boot.build.bom.Library.Module;
+import org.springframework.boot.build.bom.Library.ProhibitedVersion;
+import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
+
+/**
+ * DSL extensions for {@link BomPlugin}.
+ *
+ * @author Andy Wilkinson
+ */
+public class BomExtension {
+
+ private final Map properties = new LinkedHashMap<>();
+
+ private final Map artifactVersionProperties = new HashMap<>();
+
+ private final List libraries = new ArrayList();
+
+ private final DependencyHandler dependencyHandler;
+
+ private final UpgradeHandler upgradeHandler = new UpgradeHandler();
+
+ public BomExtension(DependencyHandler dependencyHandler) {
+ this.dependencyHandler = dependencyHandler;
+ }
+
+ public List getLibraries() {
+ return this.libraries;
+ }
+
+ public void upgrade(Closure> closure) {
+ ConfigureUtil.configure(closure, this.upgradeHandler);
+ }
+
+ public Upgrade getUpgrade() {
+ return new Upgrade(this.upgradeHandler.upgradePolicy, new GitHub(this.upgradeHandler.gitHub.organization,
+ this.upgradeHandler.gitHub.repository, this.upgradeHandler.gitHub.issueLabels));
+ }
+
+ public void library(String name, String version, Closure> closure) {
+ LibraryHandler libraryHandler = new LibraryHandler();
+ ConfigureUtil.configure(closure, libraryHandler);
+ addLibrary(new Library(name, DependencyVersion.parse(version), libraryHandler.groups,
+ libraryHandler.prohibitedVersions));
+ }
+
+ private String createDependencyNotation(String groupId, String artifactId, DependencyVersion version) {
+ return groupId + ":" + artifactId + ":" + version;
+ }
+
+ Map getProperties() {
+ return this.properties;
+ }
+
+ String getArtifactVersionProperty(String groupId, String artifactId) {
+ String coordinates = groupId + ":" + artifactId;
+ return this.artifactVersionProperties.get(coordinates);
+ }
+
+ private void putArtifactVersionProperty(String groupId, String artifactId, String versionProperty) {
+ String coordinates = groupId + ":" + artifactId;
+ String existing = this.artifactVersionProperties.putIfAbsent(coordinates, versionProperty);
+ if (existing != null) {
+ throw new InvalidUserDataException("Cannot put version property for '" + coordinates
+ + "'. Version property '" + existing + "' has already been stored.");
+ }
+ }
+
+ private void addLibrary(Library library) {
+ this.libraries.add(library);
+ this.properties.put(library.getVersionProperty(), library.getVersion());
+ for (Group group : library.getGroups()) {
+ for (Module module : group.getModules()) {
+ this.putArtifactVersionProperty(group.getId(), module.getName(), library.getVersionProperty());
+ this.dependencyHandler.getConstraints().add("api",
+ createDependencyNotation(group.getId(), module.getName(), library.getVersion()));
+ }
+ for (String bomImport : group.getBoms()) {
+ this.putArtifactVersionProperty(group.getId(), bomImport, library.getVersionProperty());
+ this.dependencyHandler.add("api", this.dependencyHandler
+ .enforcedPlatform(createDependencyNotation(group.getId(), bomImport, library.getVersion())));
+ }
+ }
+ }
+
+ public static class LibraryHandler {
+
+ private final List groups = new ArrayList<>();
+
+ private final List prohibitedVersions = new ArrayList<>();
+
+ public void group(String id, Closure> closure) {
+ GroupHandler groupHandler = new GroupHandler(id);
+ ConfigureUtil.configure(closure, groupHandler);
+ this.groups
+ .add(new Group(groupHandler.id, groupHandler.modules, groupHandler.plugins, groupHandler.imports));
+ }
+
+ public void prohibit(String range, Closure> closure) {
+ ProhibitedVersionHandler prohibitedVersionHandler = new ProhibitedVersionHandler();
+ ConfigureUtil.configure(closure, prohibitedVersionHandler);
+ try {
+ this.prohibitedVersions.add(new ProhibitedVersion(VersionRange.createFromVersionSpec(range),
+ prohibitedVersionHandler.reason));
+ }
+ catch (InvalidVersionSpecificationException ex) {
+ throw new InvalidUserCodeException("Invalid version range", ex);
+ }
+ }
+
+ public static class ProhibitedVersionHandler {
+
+ private String reason;
+
+ public void because(String because) {
+ this.reason = because;
+ }
+
+ }
+
+ public class GroupHandler extends GroovyObjectSupport {
+
+ private final String id;
+
+ private List modules = new ArrayList<>();
+
+ private List imports = new ArrayList<>();
+
+ private List plugins = new ArrayList<>();
+
+ public GroupHandler(String id) {
+ this.id = id;
+ }
+
+ public void setModules(List modules) {
+ this.modules = modules.stream()
+ .map((input) -> (input instanceof Module) ? (Module) input : new Module((String) input))
+ .collect(Collectors.toList());
+ }
+
+ public void setImports(List imports) {
+ this.imports = imports;
+ }
+
+ public void setPlugins(List plugins) {
+ this.plugins = plugins;
+ }
+
+ public Object methodMissing(String name, Object args) {
+ if (args instanceof Object[] && ((Object[]) args).length == 1) {
+ Object arg = ((Object[]) args)[0];
+ if (arg instanceof Closure) {
+ ExclusionHandler exclusionHandler = new ExclusionHandler();
+ ConfigureUtil.configure((Closure>) arg, exclusionHandler);
+ return new Module(name, exclusionHandler.exclusions);
+ }
+ }
+ throw new InvalidUserDataException("Invalid exclusion configuration for module '" + name + "'");
+ }
+
+ public class ExclusionHandler {
+
+ private final List exclusions = new ArrayList<>();
+
+ public void exclude(Map exclusion) {
+ this.exclusions.add(new Exclusion(exclusion.get("group"), exclusion.get("module")));
+ }
+
+ }
+
+ }
+
+ }
+
+ public static class UpgradeHandler {
+
+ private UpgradePolicy upgradePolicy;
+
+ private final GitHubHandler gitHub = new GitHubHandler();
+
+ public void setPolicy(UpgradePolicy upgradePolicy) {
+ this.upgradePolicy = upgradePolicy;
+ }
+
+ public void gitHub(Closure> closure) {
+ ConfigureUtil.configure(closure, this.gitHub);
+ }
+
+ }
+
+ public static final class Upgrade {
+
+ private final UpgradePolicy upgradePolicy;
+
+ private final GitHub gitHub;
+
+ private Upgrade(UpgradePolicy upgradePolicy, GitHub gitHub) {
+ this.upgradePolicy = upgradePolicy;
+ this.gitHub = gitHub;
+ }
+
+ public UpgradePolicy getPolicy() {
+ return this.upgradePolicy;
+ }
+
+ public GitHub getGitHub() {
+ return this.gitHub;
+ }
+
+ }
+
+ public static class GitHubHandler {
+
+ private String organization = "spring-projects";
+
+ private String repository = "spring-boot";
+
+ private List issueLabels;
+
+ public void setOrganization(String organization) {
+ this.organization = organization;
+ }
+
+ public void setRepository(String repository) {
+ this.repository = repository;
+ }
+
+ public void setIssueLabels(List issueLabels) {
+ this.issueLabels = issueLabels;
+ }
+
+ }
+
+ public static final class GitHub {
+
+ private String organization = "spring-projects";
+
+ private String repository = "spring-boot";
+
+ private List issueLabels;
+
+ private GitHub(String organization, String repository, List issueLabels) {
+ this.organization = organization;
+ this.repository = repository;
+ this.issueLabels = issueLabels;
+ }
+
+ public String getOrganization() {
+ return this.organization;
+ }
+
+ public String getRepository() {
+ return this.repository;
+ }
+
+ public List getIssueLabels() {
+ return this.issueLabels;
+ }
+
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/BomPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/BomPlugin.java
new file mode 100644
index 00000000000..9b08ede52d1
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/BomPlugin.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+
+import groovy.util.Node;
+import groovy.xml.QName;
+import org.gradle.api.Action;
+import org.gradle.api.GradleException;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.plugins.JavaPlatformExtension;
+import org.gradle.api.plugins.JavaPlatformPlugin;
+import org.gradle.api.plugins.PluginContainer;
+import org.gradle.api.publish.PublishingExtension;
+import org.gradle.api.publish.maven.MavenPom;
+import org.gradle.api.publish.maven.MavenPublication;
+import org.gradle.api.publish.maven.tasks.GenerateMavenPom;
+import org.gradle.api.tasks.Sync;
+import org.gradle.api.tasks.TaskExecutionException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+
+import org.springframework.boot.build.DeployedPlugin;
+import org.springframework.boot.build.MavenRepositoryPlugin;
+import org.springframework.boot.build.bom.Library.Group;
+import org.springframework.boot.build.bom.bomr.UpgradeBom;
+import org.springframework.boot.build.mavenplugin.MavenExec;
+import org.springframework.util.FileCopyUtils;
+
+/**
+ * {@link Plugin} for defining a bom. Dependencies are added as constraints in the
+ * {@code api} configuration. Imported boms are added as enforced platforms in the
+ * {@code api} configuration.
+ *
+ * @author Andy Wilkinson
+ */
+public class BomPlugin implements Plugin {
+
+ @Override
+ public void apply(Project project) {
+ PluginContainer plugins = project.getPlugins();
+ plugins.apply(DeployedPlugin.class);
+ plugins.apply(MavenRepositoryPlugin.class);
+ plugins.apply(JavaPlatformPlugin.class);
+ JavaPlatformExtension javaPlatform = project.getExtensions().getByType(JavaPlatformExtension.class);
+ javaPlatform.allowDependencies();
+ BomExtension bom = project.getExtensions().create("bom", BomExtension.class, project.getDependencies());
+ project.getTasks().create("bomrCheck", CheckBom.class, bom);
+ project.getTasks().create("bomrUpgrade", UpgradeBom.class, bom);
+ new PublishingCustomizer(project, bom).customize();
+ Configuration effectiveBomConfiguration = project.getConfigurations().create("effectiveBom");
+ project.getTasks().matching((task) -> task.getName().equals(DeployedPlugin.GENERATE_POM_TASK_NAME))
+ .all((task) -> {
+ Sync syncBom = project.getTasks().create("syncBom", Sync.class);
+ syncBom.dependsOn(task);
+ File generatedBomDir = new File(project.getBuildDir(), "generated/bom");
+ syncBom.setDestinationDir(generatedBomDir);
+ syncBom.from(((GenerateMavenPom) task).getDestination(), (pom) -> pom.rename((name) -> "pom.xml"));
+ try {
+ String settingsXmlContent = FileCopyUtils
+ .copyToString(new InputStreamReader(
+ getClass().getClassLoader().getResourceAsStream("effective-bom-settings.xml"),
+ StandardCharsets.UTF_8))
+ .replace("localRepositoryPath",
+ new File(project.getBuildDir(), "local-m2-repository").getAbsolutePath());
+ syncBom.from(project.getResources().getText().fromString(settingsXmlContent),
+ (settingsXml) -> settingsXml.rename((name) -> "settings.xml"));
+ }
+ catch (IOException ex) {
+ throw new GradleException("Failed to prepare settings.xml", ex);
+ }
+ MavenExec generateEffectiveBom = project.getTasks().create("generateEffectiveBom", MavenExec.class);
+ generateEffectiveBom.setProjectDir(generatedBomDir);
+ File effectiveBom = new File(project.getBuildDir(),
+ "generated/effective-bom/" + project.getName() + "-effective-bom.xml");
+ generateEffectiveBom.args("--settings", "settings.xml", "help:effective-pom",
+ "-Doutput=" + effectiveBom);
+ generateEffectiveBom.dependsOn(syncBom);
+ generateEffectiveBom.getOutputs().file(effectiveBom);
+ generateEffectiveBom.doLast(new StripUnrepeatableOutputAction(effectiveBom));
+ project.getArtifacts().add(effectiveBomConfiguration.getName(), effectiveBom,
+ (artifact) -> artifact.builtBy(generateEffectiveBom));
+ });
+ }
+
+ private static final class PublishingCustomizer {
+
+ private final Project project;
+
+ private final BomExtension bom;
+
+ private PublishingCustomizer(Project project, BomExtension bom) {
+ this.project = project;
+ this.bom = bom;
+ }
+
+ private void customize() {
+ PublishingExtension publishing = this.project.getExtensions().getByType(PublishingExtension.class);
+ publishing.getPublications().withType(MavenPublication.class).all(this::configurePublication);
+ }
+
+ private void configurePublication(MavenPublication publication) {
+ publication.pom(this::customizePom);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void customizePom(MavenPom pom) {
+ pom.withXml((xml) -> {
+ Node projectNode = xml.asNode();
+ Node properties = new Node(null, "properties");
+ this.bom.getProperties().forEach(properties::appendNode);
+ Node dependencyManagement = findChild(projectNode, "dependencyManagement");
+ if (dependencyManagement != null) {
+ addPropertiesBeforeDependencyManagement(projectNode, properties);
+ replaceVersionsWithVersionPropertyReferences(dependencyManagement);
+ addExclusionsToManagedDependencies(dependencyManagement);
+ }
+ else {
+ projectNode.children().add(properties);
+ }
+ addPluginManagement(projectNode);
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ private void addPropertiesBeforeDependencyManagement(Node projectNode, Node properties) {
+ for (int i = 0; i < projectNode.children().size(); i++) {
+ if (isNodeWithName(projectNode.children().get(i), "dependencyManagement")) {
+ projectNode.children().add(i, properties);
+ break;
+ }
+ }
+ }
+
+ private void replaceVersionsWithVersionPropertyReferences(Node dependencyManagement) {
+ Node dependencies = findChild(dependencyManagement, "dependencies");
+ if (dependencies != null) {
+ for (Node dependency : findChildren(dependencies, "dependency")) {
+ String groupId = findChild(dependency, "groupId").text();
+ String artifactId = findChild(dependency, "artifactId").text();
+ findChild(dependency, "version")
+ .setValue("${" + this.bom.getArtifactVersionProperty(groupId, artifactId) + "}");
+ }
+ }
+ }
+
+ private void addExclusionsToManagedDependencies(Node dependencyManagement) {
+ Node dependencies = findChild(dependencyManagement, "dependencies");
+ if (dependencies != null) {
+ for (Node dependency : findChildren(dependencies, "dependency")) {
+ String groupId = findChild(dependency, "groupId").text();
+ String artifactId = findChild(dependency, "artifactId").text();
+ this.bom.getLibraries().stream().flatMap((library) -> library.getGroups().stream())
+ .filter((group) -> group.getId().equals(groupId))
+ .flatMap((group) -> group.getModules().stream())
+ .filter((module) -> module.getName().equals(artifactId))
+ .flatMap((module) -> module.getExclusions().stream()).forEach((exclusion) -> {
+ Node exclusions = findOrCreateNode(dependency, "exclusions");
+ Node node = new Node(exclusions, "exclusion");
+ node.appendNode("groupId", exclusion.getGroupId());
+ node.appendNode("artifactId", exclusion.getArtifactId());
+ });
+ }
+ }
+ }
+
+ private void addPluginManagement(Node projectNode) {
+ for (Library library : this.bom.getLibraries()) {
+ for (Group group : library.getGroups()) {
+ Node plugins = findOrCreateNode(projectNode, "build", "pluginManagement", "plugins");
+ for (String pluginName : group.getPlugins()) {
+ Node plugin = new Node(plugins, "plugin");
+ plugin.appendNode("groupId", group.getId());
+ plugin.appendNode("artifactId", pluginName);
+ plugin.appendNode("version", "${" + library.getVersionProperty() + "}");
+ }
+ }
+ }
+ }
+
+ private Node findOrCreateNode(Node parent, String... path) {
+ Node current = parent;
+ for (String nodeName : path) {
+ Node child = findChild(current, nodeName);
+ if (child == null) {
+ child = new Node(current, nodeName);
+ }
+ current = child;
+ }
+ return current;
+ }
+
+ private Node findChild(Node parent, String name) {
+ for (Object child : parent.children()) {
+ if (child instanceof Node) {
+ Node node = (Node) child;
+ if ((node.name() instanceof QName) && name.equals(((QName) node.name()).getLocalPart())) {
+ return node;
+ }
+ if (name.equals(node.name())) {
+ return node;
+ }
+ }
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private List findChildren(Node parent, String name) {
+ return (List) parent.children().stream().filter((child) -> isNodeWithName(child, name))
+ .collect(Collectors.toList());
+
+ }
+
+ private boolean isNodeWithName(Object candidate, String name) {
+ if (candidate instanceof Node) {
+ Node node = (Node) candidate;
+ if ((node.name() instanceof QName) && name.equals(((QName) node.name()).getLocalPart())) {
+ return true;
+ }
+ if (name.equals(node.name())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ }
+
+ private static final class StripUnrepeatableOutputAction implements Action {
+
+ private final File effectiveBom;
+
+ private StripUnrepeatableOutputAction(File xmlFile) {
+ this.effectiveBom = xmlFile;
+ }
+
+ @Override
+ public void execute(Task task) {
+ try {
+ Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(this.effectiveBom);
+ XPath xpath = XPathFactory.newInstance().newXPath();
+ NodeList comments = (NodeList) xpath.evaluate("//comment()", document, XPathConstants.NODESET);
+ for (int i = 0; i < comments.getLength(); i++) {
+ org.w3c.dom.Node comment = comments.item(i);
+ comment.getParentNode().removeChild(comment);
+ }
+ org.w3c.dom.Node build = (org.w3c.dom.Node) xpath.evaluate("/project/build", document,
+ XPathConstants.NODE);
+ build.getParentNode().removeChild(build);
+ org.w3c.dom.Node reporting = (org.w3c.dom.Node) xpath.evaluate("/project/reporting", document,
+ XPathConstants.NODE);
+ reporting.getParentNode().removeChild(reporting);
+ TransformerFactory.newInstance().newTransformer().transform(new DOMSource(document),
+ new StreamResult(this.effectiveBom));
+ }
+ catch (Exception ex) {
+ throw new TaskExecutionException(task, ex);
+ }
+ }
+
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/CheckBom.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/CheckBom.java
new file mode 100644
index 00000000000..cde5d53513b
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/CheckBom.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom;
+
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.tasks.TaskAction;
+
+import org.springframework.boot.build.bom.Library.Group;
+import org.springframework.boot.build.bom.Library.Module;
+import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
+
+/**
+ * Checks the validity of a bom.
+ *
+ * @author Andy Wilkinson
+ */
+public class CheckBom extends DefaultTask {
+
+ private final BomExtension bom;
+
+ @Inject
+ public CheckBom(BomExtension bom) {
+ this.bom = bom;
+ }
+
+ @TaskAction
+ void checkBom() {
+ for (Library library : this.bom.getLibraries()) {
+ for (Group group : library.getGroups()) {
+ for (Module module : group.getModules()) {
+ if (!module.getExclusions().isEmpty()) {
+ checkExclusions(group.getId(), module, library.getVersion());
+ }
+ }
+ }
+ }
+ }
+
+ private void checkExclusions(String groupId, Module module, DependencyVersion version) {
+ Set resolved = getProject().getConfigurations()
+ .detachedConfiguration(
+ getProject().getDependencies().create(groupId + ":" + module.getName() + ":" + version))
+ .getResolvedConfiguration().getResolvedArtifacts().stream()
+ .map((artifact) -> artifact.getModuleVersion().getId())
+ .map((id) -> id.getGroup() + ":" + id.getModule().getName()).collect(Collectors.toSet());
+ Set exclusions = module.getExclusions().stream()
+ .map((exclusion) -> exclusion.getGroupId() + ":" + exclusion.getArtifactId())
+ .collect(Collectors.toSet());
+ Set unused = new TreeSet<>();
+ for (String exclusion : exclusions) {
+ if (!resolved.contains(exclusion) && exclusion.endsWith(":*")) {
+ String group = exclusion.substring(0, exclusion.indexOf(':') + 1);
+ if (!resolved.stream().filter((candidate) -> candidate.startsWith(group)).findFirst().isPresent()) {
+ unused.add(exclusion);
+ }
+ }
+ }
+ exclusions.removeAll(resolved);
+ if (!unused.isEmpty()) {
+ throw new InvalidUserDataException(
+ "Unnecessary exclusions on " + groupId + ":" + module.getName() + ": " + exclusions);
+ }
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java
new file mode 100644
index 00000000000..e28447838ca
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.maven.artifact.versioning.VersionRange;
+
+import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
+
+/**
+ * A collection of modules, Maven plugins, and Maven boms that are versioned and released
+ * together.
+ *
+ * @author Andy Wilkinson
+ */
+public class Library {
+
+ private final String name;
+
+ private final DependencyVersion version;
+
+ private final List groups;
+
+ private final String versionProperty;
+
+ private final List prohibitedVersions;
+
+ /**
+ * Create a new {@code Library} with the given {@code name}, {@code version}, and
+ * {@code groups}.
+ * @param name name of the library
+ * @param version version of the library
+ * @param groups groups in the library
+ * @param prohibitedVersions version of the library that are prohibited
+ */
+ public Library(String name, DependencyVersion version, List groups,
+ List prohibitedVersions) {
+ this.name = name;
+ this.version = version;
+ this.groups = groups;
+ this.versionProperty = name.toLowerCase(Locale.ENGLISH).replace(' ', '-') + ".version";
+ this.prohibitedVersions = prohibitedVersions;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public DependencyVersion getVersion() {
+ return this.version;
+ }
+
+ public List getGroups() {
+ return this.groups;
+ }
+
+ public String getVersionProperty() {
+ return this.versionProperty;
+ }
+
+ public List getProhibitedVersions() {
+ return this.prohibitedVersions;
+ }
+
+ /**
+ * A version or range of versions that are prohibited from being used in a bom.
+ */
+ public static class ProhibitedVersion {
+
+ private final VersionRange range;
+
+ private final String reason;
+
+ public ProhibitedVersion(VersionRange range, String reason) {
+ this.range = range;
+ this.reason = reason;
+ }
+
+ public VersionRange getRange() {
+ return this.range;
+ }
+
+ public String getReason() {
+ return this.reason;
+ }
+
+ }
+
+ /**
+ * A collection of modules, Maven plugins, and Maven boms with the same group ID.
+ */
+ public static class Group {
+
+ private final String id;
+
+ private final List modules;
+
+ private final List plugins;
+
+ private final List boms;
+
+ public Group(String id, List modules, List plugins, List boms) {
+ this.id = id;
+ this.modules = modules;
+ this.plugins = plugins;
+ this.boms = boms;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public List getModules() {
+ return this.modules;
+ }
+
+ public List getPlugins() {
+ return this.plugins;
+ }
+
+ public List getBoms() {
+ return this.boms;
+ }
+
+ }
+
+ /**
+ * A module in a group.
+ */
+ public static class Module {
+
+ private final String name;
+
+ private final List exclusions;
+
+ public Module(String name) {
+ this(name, Collections.emptyList());
+ }
+
+ public Module(String name, List exclusions) {
+ this.name = name;
+ this.exclusions = exclusions;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public List getExclusions() {
+ return this.exclusions;
+ }
+
+ }
+
+ /**
+ * An exclusion of a dependency identified by its group ID and artifact ID.
+ */
+ public static class Exclusion {
+
+ private final String groupId;
+
+ private final String artifactId;
+
+ public Exclusion(String groupId, String artifactId) {
+ this.groupId = groupId;
+ this.artifactId = artifactId;
+ }
+
+ public String getGroupId() {
+ return this.groupId;
+ }
+
+ public String getArtifactId() {
+ return this.artifactId;
+ }
+
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/UpgradePolicy.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/UpgradePolicy.java
new file mode 100644
index 00000000000..831d3b4f85c
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/UpgradePolicy.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom;
+
+import java.util.function.BiPredicate;
+
+import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
+
+/**
+ * Policies used to decide which versions are considered as possible upgrades.
+ *
+ * @author Andy Wilkinson
+ */
+public enum UpgradePolicy implements BiPredicate {
+
+ /**
+ * All versions more recent than the current version will be suggested as possible
+ * upgrades.
+ */
+ ANY((candidate, current) -> current.compareTo(candidate) < 0),
+
+ /**
+ * New minor versions of the current major version will be suggested as possible
+ * upgrades. For example, if the current version is 1.2.3, all 1.x.y versions after
+ * 1.2.3 will be suggested. 2.x versions will not be offered.
+ */
+ SAME_MAJOR_VERSION(DependencyVersion::isSameMajorAndNewerThan),
+
+ /**
+ * New patch versions of the current minor version will be offered as possible
+ * upgrades. For example, if the current version is 1.2.3, all 1.2.x versions after
+ * 1.2.3 will be suggested. 1.x versions will not be offered.
+ */
+ SAME_MINOR_VERSION(DependencyVersion::isSameMinorAndNewerThan);
+
+ private BiPredicate delegate;
+
+ UpgradePolicy(BiPredicate delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public boolean test(DependencyVersion candidate, DependencyVersion current) {
+ return this.delegate.test(candidate, current);
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/InteractiveUpgradeResolver.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/InteractiveUpgradeResolver.java
new file mode 100644
index 00000000000..b216e9e3829
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/InteractiveUpgradeResolver.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.stream.Collectors;
+
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+import org.gradle.api.internal.tasks.userinput.UserInputHandler;
+
+import org.springframework.boot.build.bom.Library;
+import org.springframework.boot.build.bom.Library.Group;
+import org.springframework.boot.build.bom.Library.Module;
+import org.springframework.boot.build.bom.Library.ProhibitedVersion;
+import org.springframework.boot.build.bom.UpgradePolicy;
+import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
+import org.springframework.util.StringUtils;
+
+/**
+ * Interactive {@link UpgradeResolver} that uses command line input to choose the upgrades
+ * to apply.
+ *
+ * @author Andy Wilkinson
+ */
+public final class InteractiveUpgradeResolver implements UpgradeResolver {
+
+ private final VersionResolver versionResolver;
+
+ private final UpgradePolicy upgradePolicy;
+
+ private final UserInputHandler userInputHandler;
+
+ InteractiveUpgradeResolver(VersionResolver versionResolver, UpgradePolicy upgradePolicy,
+ UserInputHandler userInputHandler) {
+ this.versionResolver = versionResolver;
+ this.upgradePolicy = upgradePolicy;
+ this.userInputHandler = userInputHandler;
+ }
+
+ @Override
+ public List resolveUpgrades(Collection libraries) {
+ return libraries.stream().map(this::resolveUpgrade).filter((upgrade) -> upgrade != null)
+ .collect(Collectors.toList());
+ }
+
+ private Upgrade resolveUpgrade(Library library) {
+ Map> moduleVersions = new LinkedHashMap<>();
+ for (Group group : library.getGroups()) {
+ for (Module module : group.getModules()) {
+ moduleVersions.put(group.getId() + ":" + module.getName(),
+ getLaterVersionsForModule(group.getId(), module.getName(), library.getVersion()));
+ }
+ }
+ List allVersions = moduleVersions.values().stream().flatMap(SortedSet::stream).distinct()
+ .filter((dependencyVersion) -> isPermitted(dependencyVersion, library.getProhibitedVersions()))
+ .collect(Collectors.toList());
+ if (allVersions.isEmpty()) {
+ return null;
+ }
+ List versionOptions = allVersions.stream()
+ .map((version) -> new VersionOption(version, getMissingModules(moduleVersions, version)))
+ .collect(Collectors.toList());
+ VersionOption current = new VersionOption(library.getVersion(), Collections.emptyList());
+ VersionOption selected = this.userInputHandler.selectOption(library.getName() + " " + library.getVersion(),
+ versionOptions, current);
+ return (selected.equals(current)) ? null : new Upgrade(library, selected.version);
+ }
+
+ private boolean isPermitted(DependencyVersion dependencyVersion, List prohibitedVersions) {
+ if (prohibitedVersions.isEmpty()) {
+ return true;
+ }
+ for (ProhibitedVersion prohibitedVersion : prohibitedVersions) {
+ if (prohibitedVersion.getRange()
+ .containsVersion(new DefaultArtifactVersion(dependencyVersion.toString()))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private List getMissingModules(Map> moduleVersions,
+ DependencyVersion version) {
+ List missingModules = new ArrayList<>();
+ moduleVersions.forEach((name, versions) -> {
+ if (!versions.contains(version)) {
+ missingModules.add(name);
+ }
+ });
+ return missingModules;
+ }
+
+ private SortedSet getLaterVersionsForModule(String groupId, String artifactId,
+ DependencyVersion currentVersion) {
+ SortedSet versions = this.versionResolver.resolveVersions(groupId, artifactId);
+ versions.removeIf((candidate) -> !this.upgradePolicy.test(candidate, currentVersion));
+ return versions;
+ }
+
+ private static final class VersionOption {
+
+ private final DependencyVersion version;
+
+ private final List missingModules;
+
+ private VersionOption(DependencyVersion version, List missingModules) {
+ this.version = version;
+ this.missingModules = missingModules;
+ }
+
+ @Override
+ public String toString() {
+ if (this.missingModules.isEmpty()) {
+ return this.version.toString();
+ }
+ return this.version + " (some modules are missing: "
+ + StringUtils.collectionToDelimitedString(this.missingModules, ", ") + ")";
+ }
+
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MavenMetadataVersionResolver.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MavenMetadataVersionResolver.java
new file mode 100644
index 00000000000..d29f6b07593
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MavenMetadataVersionResolver.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr;
+
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * A {@link VersionResolver} that examines {@code maven-metadata.xml} to determine the
+ * available versions.
+ *
+ * @author Andy Wilkinson
+ */
+final class MavenMetadataVersionResolver implements VersionResolver {
+
+ private final RestTemplate rest;
+
+ private final List repositoryUrls;
+
+ MavenMetadataVersionResolver(List repositoryUrls) {
+ this(new RestTemplate(Arrays.asList(new StringHttpMessageConverter())), repositoryUrls);
+ }
+
+ MavenMetadataVersionResolver(RestTemplate restTemplate, List repositoryUrls) {
+ this.rest = restTemplate;
+ this.repositoryUrls = repositoryUrls;
+ }
+
+ @Override
+ public SortedSet resolveVersions(String groupId, String artifactId) {
+ Set versions = new HashSet();
+ for (String repositoryUrl : this.repositoryUrls) {
+ versions.addAll(resolveVersions(groupId, artifactId, repositoryUrl));
+ }
+ return new TreeSet<>(versions.stream().map(DependencyVersion::parse).collect(Collectors.toSet()));
+ }
+
+ private Set resolveVersions(String groupId, String artifactId, String repositoryUrl) {
+ Set versions = new HashSet();
+ String url = repositoryUrl + "/" + groupId.replace('.', '/') + "/" + artifactId + "/maven-metadata.xml";
+ try {
+ String metadata = this.rest.getForObject(url, String.class);
+ Document metadataDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder()
+ .parse(new InputSource(new StringReader(metadata)));
+ NodeList versionNodes = (NodeList) XPathFactory.newInstance().newXPath()
+ .evaluate("/metadata/versioning/versions/version", metadataDocument, XPathConstants.NODESET);
+ for (int i = 0; i < versionNodes.getLength(); i++) {
+ versions.add(versionNodes.item(i).getTextContent());
+ }
+ }
+ catch (HttpClientErrorException ex) {
+ if (ex.getStatusCode() != HttpStatus.NOT_FOUND) {
+ System.err.println("Failed to download maven-metadata.xml for " + groupId + ":" + artifactId + " from "
+ + url + ": " + ex.getMessage());
+ }
+ }
+ catch (Exception ex) {
+ System.err.println("Failed to resolve versions for module " + groupId + ":" + artifactId + " in repository "
+ + repositoryUrl + ": " + ex.getMessage());
+ }
+ return versions;
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/Upgrade.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/Upgrade.java
new file mode 100644
index 00000000000..e0457fe8f4f
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/Upgrade.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr;
+
+import org.springframework.boot.build.bom.Library;
+import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
+
+/**
+ * An upgrade to change a {@link Library} to use a new version}.
+ *
+ * @author Andy Wilkinson
+ */
+final class Upgrade {
+
+ private final Library library;
+
+ private final DependencyVersion version;
+
+ Upgrade(Library library, DependencyVersion version) {
+ this.library = library;
+ this.version = version;
+ }
+
+ Library getLibrary() {
+ return this.library;
+ }
+
+ DependencyVersion getVersion() {
+ return this.version;
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/UpgradeBom.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/UpgradeBom.java
new file mode 100644
index 00000000000..7da198a5f1b
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/UpgradeBom.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.Properties;
+
+import javax.inject.Inject;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.Task;
+import org.gradle.api.internal.tasks.userinput.UserInputHandler;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.TaskExecutionException;
+import org.gradle.api.tasks.options.Option;
+
+import org.springframework.boot.build.bom.BomExtension;
+import org.springframework.boot.build.bom.bomr.github.GitHub;
+import org.springframework.boot.build.bom.bomr.github.GitHubRepository;
+import org.springframework.boot.build.bom.bomr.github.Milestone;
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link Task} to upgrade the libraries managed by a bom.
+ *
+ * @author Andy Wilkinson
+ */
+public class UpgradeBom extends DefaultTask {
+
+ private final BomExtension bom;
+
+ private String milestone;
+
+ @Inject
+ public UpgradeBom(BomExtension bom) {
+ this.bom = bom;
+ }
+
+ @Option(option = "milestone", description = "Milestone to which dependency upgrade issues should be assigned")
+ public void setMilestone(String milestone) {
+ this.milestone = milestone;
+ }
+
+ @TaskAction
+ void upgradeDependencies() {
+ GitHubRepository repository = createGitHub().getRepository(this.bom.getUpgrade().getGitHub().getOrganization(),
+ this.bom.getUpgrade().getGitHub().getRepository());
+ List availableLabels = repository.getLabels();
+ List issueLabels = this.bom.getUpgrade().getGitHub().getIssueLabels();
+ if (!availableLabels.containsAll(issueLabels)) {
+ List unknownLabels = new ArrayList<>(issueLabels);
+ unknownLabels.removeAll(availableLabels);
+ throw new InvalidUserDataException(
+ "Unknown label(s): " + StringUtils.collectionToCommaDelimitedString(unknownLabels));
+ }
+ Milestone milestone = determineMilestone(repository);
+ List upgrades = new InteractiveUpgradeResolver(
+ new MavenMetadataVersionResolver(Arrays.asList("https://repo1.maven.org/maven2/")),
+ this.bom.getUpgrade().getPolicy(), getServices().get(UserInputHandler.class))
+ .resolveUpgrades(this.bom.getLibraries());
+ for (Upgrade upgrade : upgrades) {
+ String title = "Upgrade to " + upgrade.getLibrary().getName() + " " + upgrade.getVersion();
+ System.out.println(title);
+ try {
+ Path buildFile = getProject().getBuildFile().toPath();
+ applyChanges(upgrade, buildFile);
+ int issueNumber = repository.openIssue(title, issueLabels, milestone);
+ if (new ProcessBuilder().command("git", "add", buildFile.toFile().getAbsolutePath()).start()
+ .waitFor() != 0) {
+ throw new IllegalStateException("git add failed");
+ }
+ if (new ProcessBuilder().command("git", "commit", "-m", title + "\n\nCloses gh-" + issueNumber).start()
+ .waitFor() != 0) {
+ throw new IllegalStateException("git commit failed");
+ }
+ }
+ catch (IOException ex) {
+ throw new TaskExecutionException(this, ex);
+ }
+ catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ private GitHub createGitHub() {
+ Properties bomrProperties = new Properties();
+ try (Reader reader = new FileReader(new File(System.getProperty("user.home"), ".bomr.properties"))) {
+ bomrProperties.load(reader);
+ String username = bomrProperties.getProperty("bomr.github.username");
+ String password = bomrProperties.getProperty("bomr.github.password");
+ return GitHub.withCredentials(username, password);
+ }
+ catch (IOException ex) {
+ throw new InvalidUserDataException("Failed to load .bomr.properties from user home", ex);
+ }
+ }
+
+ private void applyChanges(Upgrade upgrade, Path buildFile) throws IOException {
+ String contents = new String(Files.readAllBytes(buildFile), StandardCharsets.UTF_8);
+ String modified = contents.replace(
+ "library('" + upgrade.getLibrary().getName() + "', '" + upgrade.getLibrary().getVersion() + "')",
+ "library('" + upgrade.getLibrary().getName() + "', '" + upgrade.getVersion() + "')");
+ Files.write(buildFile, modified.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
+ }
+
+ private Milestone determineMilestone(GitHubRepository repository) {
+ if (this.milestone == null) {
+ return null;
+ }
+ List milestones = repository.getMilestones();
+ Optional matchingMilestone = milestones.stream()
+ .filter((milestone) -> milestone.getName().equals(this.milestone)).findFirst();
+ if (!matchingMilestone.isPresent()) {
+ throw new InvalidUserDataException("Unknown milestone: " + this.milestone);
+ }
+ return matchingMilestone.get();
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/it/executable-jar/src/main/java/org/springframework/launcher/it/jar/ExampleController.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/UpgradeResolver.java
similarity index 53%
rename from spring-boot-project/spring-boot-tools/spring-boot-loader/src/it/executable-jar/src/main/java/org/springframework/launcher/it/jar/ExampleController.java
rename to buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/UpgradeResolver.java
index 41df3759ee6..ed1805eca02 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/it/executable-jar/src/main/java/org/springframework/launcher/it/jar/ExampleController.java
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/UpgradeResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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,24 +14,25 @@
* limitations under the License.
*/
-package org.springframework.boot.load.it.jar;
+package org.springframework.boot.build.bom.bomr;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
+import java.util.Collection;
+import java.util.List;
+
+import org.springframework.boot.build.bom.Library;
/**
- * Simple example Spring MVC Controller.
+ * Resolves upgrades for the libraries in a bom.
*
- * @author Phillip Webb
+ * @author Andy Wilkinson
*/
-@Controller
-public class ExampleController {
+interface UpgradeResolver {
- @RequestMapping("/")
- @ResponseBody
- public String helloWorld() {
- return "Hello Embedded Jar World!";
- }
+ /**
+ * Resolves the upgrades to be applied to the given {@code libraries}.
+ * @param libraries the libraries
+ * @return the upgrades
+ */
+ List resolveUpgrades(Collection libraries);
}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/VersionResolver.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/VersionResolver.java
new file mode 100644
index 00000000000..70b1c9298cf
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/VersionResolver.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr;
+
+import java.util.SortedSet;
+
+import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
+
+/**
+ * Resolves the available versions for a module.
+ *
+ * @author Andy Wilkinson
+ */
+interface VersionResolver {
+
+ /**
+ * Resolves the available versions for the module identified by the given
+ * {@code groupId} and {@code artifactId}.
+ * @param groupId module's group ID
+ * @param artifactId module's artifact ID
+ * @return the available versions
+ */
+ SortedSet resolveVersions(String groupId, String artifactId);
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/GitHub.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/GitHub.java
new file mode 100644
index 00000000000..879caec26ef
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/GitHub.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.github;
+
+/**
+ * Minimal API for interacting with GitHub.
+ *
+ * @author Andy Wilkinson
+ */
+public interface GitHub {
+
+ /**
+ * Returns a {@link GitHubRepository} with the given {@code name} in the given
+ * {@code organization}.
+ * @param organization the organization
+ * @param name the name of the repository
+ * @return the repository
+ */
+ GitHubRepository getRepository(String organization, String name);
+
+ /**
+ * Creates a new {@code GitHub} that will authenticate with given {@code username} and
+ * {@code password}.
+ * @param username username for authentication
+ * @param password password for authentication
+ * @return the new {@code GitHub} instance
+ */
+ static GitHub withCredentials(String username, String password) {
+ return new StandardGitHub(username, password);
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/GitHubRepository.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/GitHubRepository.java
new file mode 100644
index 00000000000..3c49ba56d55
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/GitHubRepository.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.github;
+
+import java.util.List;
+
+/**
+ * Minimal API for interacting with a GitHub repository.
+ *
+ * @author Andy Wilkinson
+ */
+public interface GitHubRepository {
+
+ /**
+ * Opens a new issue with the given title. The given {@code labels} will be applied to
+ * the issue and it will be assigned to the given {@code milestone}.
+ * @param title the title of the issue
+ * @param labels the labels to apply to the issue
+ * @param milestone the milestone to assign the issue to
+ * @return the number of the new issue
+ */
+ int openIssue(String title, List labels, Milestone milestone);
+
+ /**
+ * Returns the labels in the repository.
+ * @return the labels
+ */
+ List getLabels();
+
+ /**
+ * Returns the milestones in the repository.
+ * @return the milestones
+ */
+ List getMilestones();
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/Milestone.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/Milestone.java
new file mode 100644
index 00000000000..f50dd28c48d
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/Milestone.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.github;
+
+/**
+ * A milestone in a {@link GitHubRepository GitHub repository}.
+ *
+ * @author Andy Wilkinson
+ */
+public class Milestone {
+
+ private final String name;
+
+ private final int number;
+
+ Milestone(String name, int number) {
+ this.name = name;
+ this.number = number;
+ }
+
+ /**
+ * Returns the name of the milestone.
+ * @return the name
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * Returns the number of the milestone.
+ * @return the number
+ */
+ public int getNumber() {
+ return this.number;
+ }
+
+ @Override
+ public String toString() {
+ return this.name + " (" + this.number + ")";
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHub.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHub.java
new file mode 100644
index 00000000000..5be432a0ac9
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHub.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.github;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Base64;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.springframework.http.HttpRequest;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.DefaultUriBuilderFactory;
+import org.springframework.web.util.UriTemplateHandler;
+
+/**
+ * Standard implementation of {@link GitHub}.
+ *
+ * @author Andy Wilkinson
+ */
+final class StandardGitHub implements GitHub {
+
+ private final String username;
+
+ private final String password;
+
+ StandardGitHub(String username, String password) {
+ this.username = username;
+ this.password = password;
+ }
+
+ @Override
+ public GitHubRepository getRepository(String organization, String name) {
+ RestTemplate restTemplate = new RestTemplate(
+ Arrays.asList(new MappingJackson2HttpMessageConverter(new ObjectMapper())));
+ restTemplate.getInterceptors().add(new ClientHttpRequestInterceptor() {
+
+ @Override
+ public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
+ throws IOException {
+ request.getHeaders().add("User-Agent", StandardGitHub.this.username);
+ request.getHeaders().add("Authorization", "Basic " + Base64.getEncoder().encodeToString(
+ (StandardGitHub.this.username + ":" + StandardGitHub.this.password).getBytes()));
+ request.getHeaders().add("Accept", MediaType.APPLICATION_JSON_VALUE);
+ return execution.execute(request, body);
+ }
+
+ });
+ UriTemplateHandler uriTemplateHandler = new DefaultUriBuilderFactory(
+ "https://api.github.com/repos/" + organization + "/" + name + "/");
+ restTemplate.setUriTemplateHandler(uriTemplateHandler);
+ return new StandardGitHubRepository(restTemplate);
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHubRepository.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHubRepository.java
new file mode 100644
index 00000000000..6eced352719
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHubRepository.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.github;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Standard implementation of {@link GitHubRepository}.
+ *
+ * @author Andy Wilkinson
+ */
+final class StandardGitHubRepository implements GitHubRepository {
+
+ private final RestTemplate rest;
+
+ StandardGitHubRepository(RestTemplate restTemplate) {
+ this.rest = restTemplate;
+ }
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ public int openIssue(String title, List labels, Milestone milestone) {
+ Map body = new HashMap<>();
+ body.put("title", title);
+ if (milestone != null) {
+ body.put("milestone", milestone.getNumber());
+ }
+ if (!labels.isEmpty()) {
+ body.put("labels", labels);
+ }
+ ResponseEntity response = this.rest.postForEntity("issues", body, Map.class);
+ return (Integer) response.getBody().get("number");
+ }
+
+ @Override
+ public List getLabels() {
+ return get("labels?per_page=100", (label) -> (String) label.get("name"));
+ }
+
+ @Override
+ public List getMilestones() {
+ return get("milestones?per_page=100",
+ (milestone) -> new Milestone((String) milestone.get("title"), (Integer) milestone.get("number")));
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private List get(String name, Function, T> mapper) {
+ ResponseEntity response = this.rest.getForEntity(name, List.class);
+ List> body = response.getBody();
+ return body.stream().map(mapper).collect(Collectors.toList());
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/AbstractDependencyVersion.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/AbstractDependencyVersion.java
new file mode 100644
index 00000000000..0192c64f6f5
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/AbstractDependencyVersion.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.version;
+
+import org.apache.maven.artifact.versioning.ComparableVersion;
+
+/**
+ * Base class for {@link DependencyVersion} implementations.
+ *
+ * @author Andy Wilkinson
+ */
+abstract class AbstractDependencyVersion implements DependencyVersion {
+
+ private final ComparableVersion comparableVersion;
+
+ protected AbstractDependencyVersion(ComparableVersion comparableVersion) {
+ this.comparableVersion = comparableVersion;
+ }
+
+ @Override
+ public int compareTo(DependencyVersion other) {
+ ComparableVersion otherComparable = (other instanceof AbstractDependencyVersion)
+ ? ((AbstractDependencyVersion) other).comparableVersion : new ComparableVersion(other.toString());
+ return this.comparableVersion.compareTo(otherComparable);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ AbstractDependencyVersion other = (AbstractDependencyVersion) obj;
+ if (!this.comparableVersion.equals(other.comparableVersion)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.comparableVersion.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return this.comparableVersion.toString();
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/ArtifactVersionDependencyVersion.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/ArtifactVersionDependencyVersion.java
new file mode 100644
index 00000000000..8ff9408ef9b
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/ArtifactVersionDependencyVersion.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.version;
+
+import java.util.Optional;
+
+import org.apache.maven.artifact.versioning.ArtifactVersion;
+import org.apache.maven.artifact.versioning.ComparableVersion;
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+
+/**
+ * A {@link DependencyVersion} backed by an {@link ArtifactVersion}.
+ *
+ * @author Andy Wilkinson
+ */
+class ArtifactVersionDependencyVersion extends AbstractDependencyVersion {
+
+ private final ArtifactVersion artifactVersion;
+
+ protected ArtifactVersionDependencyVersion(ArtifactVersion artifactVersion) {
+ super(new ComparableVersion(artifactVersion.toString()));
+ this.artifactVersion = artifactVersion;
+ }
+
+ protected ArtifactVersionDependencyVersion(ArtifactVersion artifactVersion, ComparableVersion comparableVersion) {
+ super(comparableVersion);
+ this.artifactVersion = artifactVersion;
+ }
+
+ @Override
+ public boolean isNewerThan(DependencyVersion other) {
+ if (other instanceof ReleaseTrainDependencyVersion) {
+ return false;
+ }
+ return compareTo(other) > 0;
+ }
+
+ @Override
+ public boolean isSameMajorAndNewerThan(DependencyVersion other) {
+ if (other instanceof ReleaseTrainDependencyVersion) {
+ return false;
+ }
+ return extractArtifactVersionDependencyVersion(other).map(this::isSameMajorAndNewerThan).orElse(true);
+ }
+
+ private boolean isSameMajorAndNewerThan(ArtifactVersionDependencyVersion other) {
+ return this.artifactVersion.getMajorVersion() == other.artifactVersion.getMajorVersion() && isNewerThan(other);
+ }
+
+ @Override
+ public boolean isSameMinorAndNewerThan(DependencyVersion other) {
+ if (other instanceof ReleaseTrainDependencyVersion) {
+ return false;
+ }
+ return extractArtifactVersionDependencyVersion(other).map(this::isSameMinorAndNewerThan).orElse(true);
+ }
+
+ private boolean isSameMinorAndNewerThan(ArtifactVersionDependencyVersion other) {
+ return this.artifactVersion.getMajorVersion() == other.artifactVersion.getMajorVersion()
+ && this.artifactVersion.getMinorVersion() == other.artifactVersion.getMinorVersion()
+ && isNewerThan(other);
+ }
+
+ @Override
+ public String toString() {
+ return this.artifactVersion.toString();
+ }
+
+ private Optional extractArtifactVersionDependencyVersion(
+ DependencyVersion other) {
+ ArtifactVersionDependencyVersion artifactVersion = null;
+ if (other instanceof ArtifactVersionDependencyVersion) {
+ artifactVersion = (ArtifactVersionDependencyVersion) other;
+ }
+ return Optional.ofNullable(artifactVersion);
+ }
+
+ static ArtifactVersionDependencyVersion parse(String version) {
+ ArtifactVersion artifactVersion = new DefaultArtifactVersion(version);
+ if (artifactVersion.getQualifier() != null && artifactVersion.getQualifier().equals(version)) {
+ return null;
+ }
+ return new ArtifactVersionDependencyVersion(artifactVersion);
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/CombinedPatchAndQualifierDependencyVersion.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/CombinedPatchAndQualifierDependencyVersion.java
new file mode 100644
index 00000000000..8a910d6b60b
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/CombinedPatchAndQualifierDependencyVersion.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.version;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.maven.artifact.versioning.ArtifactVersion;
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+
+/**
+ * A {@link DependencyVersion} where the patch and qualifier are not separated.
+ *
+ * @author Andy Wilkinson
+ */
+final class CombinedPatchAndQualifierDependencyVersion extends ArtifactVersionDependencyVersion {
+
+ private static final Pattern PATTERN = Pattern.compile("([0-9]+\\.[0-9]+\\.[0-9]+)([A-Za-z][A-Za-z0-9]+)");
+
+ private final String original;
+
+ private CombinedPatchAndQualifierDependencyVersion(ArtifactVersion artifactVersion, String original) {
+ super(artifactVersion);
+ this.original = original;
+ }
+
+ @Override
+ public String toString() {
+ return this.original;
+ }
+
+ static CombinedPatchAndQualifierDependencyVersion parse(String version) {
+ Matcher matcher = PATTERN.matcher(version);
+ if (!matcher.matches()) {
+ return null;
+ }
+ ArtifactVersion artifactVersion = new DefaultArtifactVersion(matcher.group(1) + "." + matcher.group(2));
+ if (artifactVersion.getQualifier() != null && artifactVersion.getQualifier().equals(version)) {
+ return null;
+ }
+ return new CombinedPatchAndQualifierDependencyVersion(artifactVersion, version);
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/DependencyVersion.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/DependencyVersion.java
new file mode 100644
index 00000000000..a344a5c0e68
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/DependencyVersion.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.version;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Version of a dependency.
+ *
+ * @author Andy Wilkinson
+ */
+public interface DependencyVersion extends Comparable {
+
+ /**
+ * Returns whether this version is newer than the given {@code other} version.
+ * @param other version to test
+ * @return {@code true} if this version is newer, otherwise {@code false}
+ */
+ boolean isNewerThan(DependencyVersion other);
+
+ /**
+ * Returns whether this version has the same major versions as the {@code other}
+ * version while also being newer.
+ * @param other version to test
+ * @return {@code true} if this version has the same major and is newer, otherwise
+ * {@code false}
+ */
+ boolean isSameMajorAndNewerThan(DependencyVersion other);
+
+ /**
+ * Returns whether this version has the same major and minor versions as the
+ * {@code other} version while also being newer.
+ * @param other version to test
+ * @return {@code true} if this version has the same major and minor and is newer,
+ * otherwise {@code false}
+ */
+ boolean isSameMinorAndNewerThan(DependencyVersion other);
+
+ static DependencyVersion parse(String version) {
+ List> parsers = Arrays.asList(ArtifactVersionDependencyVersion::parse,
+ ReleaseTrainDependencyVersion::parse, NumericQualifierDependencyVersion::parse,
+ CombinedPatchAndQualifierDependencyVersion::parse, LeadingZeroesDependencyVersion::parse,
+ UnstructuredDependencyVersion::parse);
+ for (Function parser : parsers) {
+ DependencyVersion result = parser.apply(version);
+ if (result != null) {
+ return result;
+ }
+ }
+ throw new IllegalArgumentException("Version '" + version + "' could not be parsed");
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/LeadingZeroesDependencyVersion.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/LeadingZeroesDependencyVersion.java
new file mode 100644
index 00000000000..5514b8b652a
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/LeadingZeroesDependencyVersion.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.version;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.maven.artifact.versioning.ArtifactVersion;
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+
+/**
+ * A {@link DependencyVersion} that tolerates leading zeroes.
+ *
+ * @author Andy Wilkinson
+ */
+final class LeadingZeroesDependencyVersion extends ArtifactVersionDependencyVersion {
+
+ private static final Pattern PATTERN = Pattern.compile("0*([0-9]+)\\.0*([0-9]+)\\.0*([0-9]+)");
+
+ private final String original;
+
+ private LeadingZeroesDependencyVersion(ArtifactVersion artifactVersion, String original) {
+ super(artifactVersion);
+ this.original = original;
+ }
+
+ @Override
+ public String toString() {
+ return this.original;
+ }
+
+ static LeadingZeroesDependencyVersion parse(String input) {
+ Matcher matcher = PATTERN.matcher(input);
+ if (!matcher.matches()) {
+ return null;
+ }
+ ArtifactVersion artifactVersion = new DefaultArtifactVersion(
+ matcher.group(1) + matcher.group(2) + matcher.group(3));
+ return new LeadingZeroesDependencyVersion(artifactVersion, input);
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/NumericQualifierDependencyVersion.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/NumericQualifierDependencyVersion.java
new file mode 100644
index 00000000000..363def7d801
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/NumericQualifierDependencyVersion.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.version;
+
+import org.apache.maven.artifact.versioning.ArtifactVersion;
+import org.apache.maven.artifact.versioning.ComparableVersion;
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+
+/**
+ * A fallback {@link DependencyVersion} to handle versions with four components that
+ * cannot be handled by {@link ArtifactVersion} because the fourth component is numeric.
+ *
+ * @author Andy Wilkinson
+ */
+final class NumericQualifierDependencyVersion extends ArtifactVersionDependencyVersion {
+
+ private final String original;
+
+ private NumericQualifierDependencyVersion(ArtifactVersion artifactVersion, String original) {
+ super(artifactVersion, new ComparableVersion(original));
+ this.original = original;
+ }
+
+ @Override
+ public String toString() {
+ return this.original;
+ }
+
+ static NumericQualifierDependencyVersion parse(String input) {
+ String[] components = input.split("\\.");
+ if (components.length == 4) {
+ ArtifactVersion artifactVersion = new DefaultArtifactVersion(
+ components[0] + "." + components[1] + "." + components[2]);
+ if (artifactVersion.getQualifier() != null && artifactVersion.getQualifier().equals(input)) {
+ return null;
+ }
+ return new NumericQualifierDependencyVersion(artifactVersion, input);
+ }
+ return null;
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/ReleaseTrainDependencyVersion.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/ReleaseTrainDependencyVersion.java
new file mode 100644
index 00000000000..b3626d19846
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/ReleaseTrainDependencyVersion.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.version;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * A {@link DependencyVersion} for a release train such as Spring Data.
+ *
+ * @author Andy Wilkinson
+ */
+final class ReleaseTrainDependencyVersion implements DependencyVersion {
+
+ private static final Pattern VERSION_PATTERN = Pattern.compile("([A-Z][a-z]+)-([A-Z]+)([0-9]*)");
+
+ private final String releaseTrain;
+
+ private final String type;
+
+ private final int version;
+
+ private final String original;
+
+ private ReleaseTrainDependencyVersion(String releaseTrain, String type, int version, String original) {
+ this.releaseTrain = releaseTrain;
+ this.type = type;
+ this.version = version;
+ this.original = original;
+ }
+
+ @Override
+ public int compareTo(DependencyVersion other) {
+ if (!(other instanceof ReleaseTrainDependencyVersion)) {
+ return 0;
+ }
+ ReleaseTrainDependencyVersion otherReleaseTrain = (ReleaseTrainDependencyVersion) other;
+ int comparison = this.releaseTrain.compareTo(otherReleaseTrain.releaseTrain);
+ if (comparison != 0) {
+ return comparison;
+ }
+ comparison = this.type.compareTo(otherReleaseTrain.type);
+ if (comparison != 0) {
+ return comparison;
+ }
+ return Integer.compare(this.version, otherReleaseTrain.version);
+ }
+
+ @Override
+ public boolean isNewerThan(DependencyVersion other) {
+ if (!(other instanceof ReleaseTrainDependencyVersion)) {
+ return true;
+ }
+ ReleaseTrainDependencyVersion otherReleaseTrain = (ReleaseTrainDependencyVersion) other;
+ return otherReleaseTrain.compareTo(this) < 0;
+ }
+
+ @Override
+ public boolean isSameMajorAndNewerThan(DependencyVersion other) {
+ return isNewerThan(other);
+ }
+
+ @Override
+ public boolean isSameMinorAndNewerThan(DependencyVersion other) {
+ if (!(other instanceof ReleaseTrainDependencyVersion)) {
+ return true;
+ }
+ ReleaseTrainDependencyVersion otherReleaseTrain = (ReleaseTrainDependencyVersion) other;
+ return otherReleaseTrain.releaseTrain.equals(this.releaseTrain) && isNewerThan(other);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ReleaseTrainDependencyVersion other = (ReleaseTrainDependencyVersion) obj;
+ if (!this.original.equals(other.original)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.original.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return this.original;
+ }
+
+ static ReleaseTrainDependencyVersion parse(String input) {
+ Matcher matcher = VERSION_PATTERN.matcher(input);
+ if (!matcher.matches()) {
+ return null;
+ }
+ return new ReleaseTrainDependencyVersion(matcher.group(1), matcher.group(2),
+ (StringUtils.hasLength(matcher.group(3))) ? Integer.parseInt(matcher.group(3)) : 0, input);
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/UnstructuredDependencyVersion.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/UnstructuredDependencyVersion.java
new file mode 100644
index 00000000000..02822beaec9
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/UnstructuredDependencyVersion.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.version;
+
+import org.apache.maven.artifact.versioning.ComparableVersion;
+
+/**
+ * A {@link DependencyVersion} with no structure such that version comparisons are not
+ * possible.
+ *
+ * @author Andy Wilkinson
+ */
+final class UnstructuredDependencyVersion extends AbstractDependencyVersion implements DependencyVersion {
+
+ private final String version;
+
+ private UnstructuredDependencyVersion(String version) {
+ super(new ComparableVersion(version));
+ this.version = version;
+ }
+
+ @Override
+ public boolean isNewerThan(DependencyVersion other) {
+ return this.compareTo(other) > 0;
+ }
+
+ @Override
+ public boolean isSameMajorAndNewerThan(DependencyVersion other) {
+ return this.compareTo(other) > 0;
+ }
+
+ @Override
+ public boolean isSameMinorAndNewerThan(DependencyVersion other) {
+ return this.compareTo(other) > 0;
+ }
+
+ @Override
+ public String toString() {
+ return this.version;
+ }
+
+ static UnstructuredDependencyVersion parse(String version) {
+ return new UnstructuredDependencyVersion(version);
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/classpath/CheckClasspathForConflicts.java b/buildSrc/src/main/java/org/springframework/boot/build/classpath/CheckClasspathForConflicts.java
new file mode 100644
index 00000000000..b5096ed1159
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/classpath/CheckClasspathForConflicts.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.classpath;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.function.Predicate;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.stream.Collectors;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.GradleException;
+import org.gradle.api.Task;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.tasks.Classpath;
+import org.gradle.api.tasks.TaskAction;
+
+/**
+ * A {@link Task} for checking the classpath for conflicting classes and resources.
+ *
+ * @author Andy Wilkinson
+ */
+public class CheckClasspathForConflicts extends DefaultTask {
+
+ private final List> ignores = new ArrayList<>();
+
+ private FileCollection classpath;
+
+ public void setClasspath(FileCollection classpath) {
+ this.classpath = classpath;
+ }
+
+ @Classpath
+ public FileCollection getClasspath() {
+ return this.classpath;
+ }
+
+ @TaskAction
+ public void checkForConflicts() throws IOException {
+ ClasspathContents classpathContents = new ClasspathContents();
+ for (File file : this.classpath) {
+ if (file.isDirectory()) {
+ Path root = file.toPath();
+ Files.walk(root).filter((path) -> Files.isRegularFile(path))
+ .forEach((entry) -> classpathContents.add(root.relativize(entry).toString(), root.toString()));
+ }
+ else {
+ try (JarFile jar = new JarFile(file)) {
+ for (JarEntry entry : Collections.list(jar.entries())) {
+ if (!entry.isDirectory()) {
+ classpathContents.add(entry.getName(), file.getAbsolutePath());
+ }
+ }
+ }
+ }
+ }
+ Map> conflicts = classpathContents.getConflicts(this.ignores);
+ if (!conflicts.isEmpty()) {
+ StringBuilder message = new StringBuilder(String.format("Found classpath conflicts:%n"));
+ conflicts.forEach((entry, locations) -> {
+ message.append(String.format(" %s%n", entry));
+ locations.forEach((location) -> message.append(String.format(" %s%n", location)));
+ });
+ throw new GradleException(message.toString());
+ }
+ }
+
+ public void ignore(Predicate predicate) {
+ this.ignores.add(predicate);
+ }
+
+ private static final class ClasspathContents {
+
+ private static final Set IGNORED_NAMES = new HashSet<>(Arrays.asList("about.html", "changelog.txt",
+ "LICENSE", "license.txt", "module-info.class", "notice.txt", "readme.txt"));
+
+ private final Map> classpathContents = new HashMap<>();
+
+ private void add(String name, String source) {
+ this.classpathContents.computeIfAbsent(name, (key) -> new ArrayList<>()).add(source);
+ }
+
+ private Map> getConflicts(List> ignores) {
+ return this.classpathContents.entrySet().stream().filter((entry) -> entry.getValue().size() > 1)
+ .filter((entry) -> canConflict(entry.getKey(), ignores))
+ .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (v1, v2) -> v1, TreeMap::new));
+ }
+
+ private boolean canConflict(String name, List> ignores) {
+ if (name.startsWith("META-INF/")) {
+ return false;
+ }
+ for (String ignoredName : IGNORED_NAMES) {
+ if (name.equals(ignoredName)) {
+ return false;
+ }
+ }
+ for (Predicate ignore : ignores) {
+ if (ignore.test(name)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/classpath/CheckClasspathForProhibitedDependencies.java b/buildSrc/src/main/java/org/springframework/boot/build/classpath/CheckClasspathForProhibitedDependencies.java
new file mode 100644
index 00000000000..301304deb71
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/classpath/CheckClasspathForProhibitedDependencies.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.classpath;
+
+import java.io.IOException;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.GradleException;
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.tasks.Classpath;
+import org.gradle.api.tasks.TaskAction;
+
+/**
+ * A {@link Task} for checking the classpath for prohibited dependencies.
+ *
+ * @author Andy Wilkinson
+ */
+public class CheckClasspathForProhibitedDependencies extends DefaultTask {
+
+ private Configuration classpath;
+
+ public CheckClasspathForProhibitedDependencies() {
+ getOutputs().upToDateWhen((task) -> true);
+ }
+
+ public void setClasspath(Configuration classpath) {
+ this.classpath = classpath;
+ }
+
+ @Classpath
+ public FileCollection getClasspath() {
+ return this.classpath;
+ }
+
+ @TaskAction
+ public void checkForProhibitedDependencies() throws IOException {
+ TreeSet prohibited = this.classpath.getResolvedConfiguration().getResolvedArtifacts().stream()
+ .map((artifact) -> artifact.getModuleVersion().getId()).filter(this::prohibited)
+ .map((id) -> id.getGroup() + ":" + id.getName()).collect(Collectors.toCollection(TreeSet::new));
+ if (!prohibited.isEmpty()) {
+ StringBuilder message = new StringBuilder(String.format("Found prohibited dependencies:%n"));
+ for (String dependency : prohibited) {
+ message.append(String.format(" %s%n", dependency));
+ }
+ throw new GradleException(message.toString());
+ }
+ }
+
+ private boolean prohibited(ModuleVersionIdentifier id) {
+ String group = id.getGroup();
+ if (group.equals("javax.batch")) {
+ return false;
+ }
+ if (group.startsWith("javax")) {
+ return true;
+ }
+ if (group.equals("commons-logging")) {
+ return true;
+ }
+ if (group.equals("org.slf4j") && id.getName().equals("jcl-over-slf4j")) {
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/cli/AbstractPackageManagerDefinitionTask.java b/buildSrc/src/main/java/org/springframework/boot/build/cli/AbstractPackageManagerDefinitionTask.java
new file mode 100644
index 00000000000..f0592979d39
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/cli/AbstractPackageManagerDefinitionTask.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 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 org.springframework.boot.build.cli;
+
+import java.io.File;
+import java.security.MessageDigest;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Project;
+import org.gradle.api.file.RegularFile;
+import org.gradle.api.provider.Provider;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskExecutionException;
+
+/**
+ * Base class for generating a package manager definition file such as a Scoop manifest or
+ * a Homebrew formula.
+ *
+ * @author Andy Wilkinson
+ */
+public abstract class AbstractPackageManagerDefinitionTask extends DefaultTask {
+
+ private Provider archive;
+
+ private File template;
+
+ private File outputDir;
+
+ public AbstractPackageManagerDefinitionTask() {
+ getInputs().property("version", getProject().provider(getProject()::getVersion));
+ }
+
+ @InputFile
+ public RegularFile getArchive() {
+ return this.archive.get();
+ }
+
+ public void setArchive(Provider archive) {
+ this.archive = archive;
+ }
+
+ @InputFile
+ public File getTemplate() {
+ return this.template;
+ }
+
+ public void setTemplate(File template) {
+ this.template = template;
+ }
+
+ @OutputDirectory
+ public File getOutputDir() {
+ return this.outputDir;
+ }
+
+ public void setOutputDir(File outputDir) {
+ this.outputDir = outputDir;
+ }
+
+ protected void createDescriptor(Map additionalProperties) {
+ getProject().copy((copy) -> {
+ copy.from(this.template);
+ copy.into(this.outputDir);
+ Map properties = new HashMap<>(additionalProperties);
+ properties.put("hash", sha256(this.archive.get().getAsFile()));
+ properties.put("repo", determineArtifactoryRepo(getProject()));
+ properties.put("project", getProject());
+ copy.expand(properties);
+ });
+ }
+
+ private String sha256(File file) {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ return new DigestUtils(digest).digestAsHex(file);
+ }
+ catch (Exception ex) {
+ throw new TaskExecutionException(this, ex);
+ }
+ }
+
+ private String determineArtifactoryRepo(Project project) {
+ String version = project.getVersion().toString();
+ String type = version.substring(version.lastIndexOf('.'));
+ if (type.equals("RELEASE")) {
+ return "release";
+ }
+ if (type.startsWith("M") || type.startsWith("RC")) {
+ return "milestone";
+ }
+ return "snapshot";
+ }
+
+}
diff --git a/spring-boot-tests/spring-boot-deployment-tests/spring-boot-deployment-test-wildfly/src/main/java/sample/SampleController.java b/buildSrc/src/main/java/org/springframework/boot/build/cli/HomebrewFormula.java
similarity index 57%
rename from spring-boot-tests/spring-boot-deployment-tests/spring-boot-deployment-test-wildfly/src/main/java/sample/SampleController.java
rename to buildSrc/src/main/java/org/springframework/boot/build/cli/HomebrewFormula.java
index 819f68150b4..5592d306175 100644
--- a/spring-boot-tests/spring-boot-deployment-tests/spring-boot-deployment-test-wildfly/src/main/java/sample/SampleController.java
+++ b/buildSrc/src/main/java/org/springframework/boot/build/cli/HomebrewFormula.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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,17 +14,22 @@
* limitations under the License.
*/
-package sample;
+package org.springframework.boot.build.cli;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RestController;
+import java.util.Collections;
-@RestController
-public class SampleController {
+import org.gradle.api.tasks.TaskAction;
- @GetMapping("/")
- public String hello() {
- return "Hello World";
+/**
+ * A {@Task} for creating a Homebrew formula manifest.
+ *
+ * @author Andy Wilkinson
+ */
+public class HomebrewFormula extends AbstractPackageManagerDefinitionTask {
+
+ @TaskAction
+ void createFormula() {
+ createDescriptor(Collections.emptyMap());
}
}
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/it/executable-dir/src/main/java/org/springframework/launcher/it/jar/ExampleController.java b/buildSrc/src/main/java/org/springframework/boot/build/cli/ScoopManifest.java
similarity index 53%
rename from spring-boot-project/spring-boot-tools/spring-boot-loader/src/it/executable-dir/src/main/java/org/springframework/launcher/it/jar/ExampleController.java
rename to buildSrc/src/main/java/org/springframework/boot/build/cli/ScoopManifest.java
index 41df3759ee6..de671c8da14 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/it/executable-dir/src/main/java/org/springframework/launcher/it/jar/ExampleController.java
+++ b/buildSrc/src/main/java/org/springframework/boot/build/cli/ScoopManifest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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,24 +14,23 @@
* limitations under the License.
*/
-package org.springframework.boot.load.it.jar;
+package org.springframework.boot.build.cli;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
+import java.util.Collections;
+
+import org.gradle.api.tasks.TaskAction;
/**
- * Simple example Spring MVC Controller.
+ * A {@Task} for creating a Scoop manifest.
*
- * @author Phillip Webb
+ * @author Andy Wilkinson
*/
-@Controller
-public class ExampleController {
+public class ScoopManifest extends AbstractPackageManagerDefinitionTask {
- @RequestMapping("/")
- @ResponseBody
- public String helloWorld() {
- return "Hello Embedded Jar World!";
+ @TaskAction
+ void createManifest() {
+ String version = getProject().getVersion().toString();
+ createDescriptor(Collections.singletonMap("scoopVersion", version.substring(0, version.lastIndexOf('.'))));
}
}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/constraints/DocumentConstrainedVersions.java b/buildSrc/src/main/java/org/springframework/boot/build/constraints/DocumentConstrainedVersions.java
new file mode 100644
index 00000000000..101039e9fd8
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/constraints/DocumentConstrainedVersions.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 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 org.springframework.boot.build.constraints;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.inject.Inject;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.model.ObjectFactory;
+import org.gradle.api.provider.SetProperty;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.TaskAction;
+
+import org.springframework.boot.build.constraints.ExtractVersionConstraints.ConstrainedVersion;
+
+/**
+ * Task for documenting a platform's constrained versions.
+ *
+ * @author Andy Wilkinson
+ */
+public class DocumentConstrainedVersions extends DefaultTask {
+
+ private final SetProperty constrainedVersions;
+
+ private File outputFile;
+
+ @Inject
+ public DocumentConstrainedVersions(ObjectFactory objectFactory) {
+ this.constrainedVersions = objectFactory.setProperty(ConstrainedVersion.class);
+ }
+
+ @Input
+ public SetProperty getConstrainedVersions() {
+ return this.constrainedVersions;
+ }
+
+ @OutputFile
+ public File getOutputFile() {
+ return this.outputFile;
+ }
+
+ public void setOutputFile(File outputFile) {
+ this.outputFile = outputFile;
+ }
+
+ @TaskAction
+ public void documentConstrainedVersions() throws IOException {
+ this.outputFile.getParentFile().mkdirs();
+ try (PrintWriter writer = new PrintWriter(new FileWriter(this.outputFile))) {
+ writer.println("|===");
+ writer.println("| Group ID | Artifact ID | Version");
+ for (ConstrainedVersion constrainedVersion : this.constrainedVersions.get()) {
+ writer.println();
+ writer.printf("| `%s`%n", constrainedVersion.getGroup());
+ writer.printf("| `%s`%n", constrainedVersion.getArtifact());
+ writer.printf("| `%s`%n", constrainedVersion.getVersion());
+ }
+ writer.println("|===");
+ }
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/constraints/ExtractVersionConstraints.java b/buildSrc/src/main/java/org/springframework/boot/build/constraints/ExtractVersionConstraints.java
new file mode 100644
index 00000000000..3151560b7f6
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/constraints/ExtractVersionConstraints.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 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 org.springframework.boot.build.constraints;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.ComponentMetadataDetails;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.DependencyConstraint;
+import org.gradle.api.artifacts.DependencyConstraintMetadata;
+import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.api.internal.AbstractTask;
+import org.gradle.api.tasks.Internal;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.platform.base.Platform;
+
+/**
+ * {@link Task} to extract constraints from a {@link Platform}. The platform's own
+ * constraints and those in any boms upon which it depends are extracted.
+ *
+ * @author Andy Wilkinson
+ */
+public class ExtractVersionConstraints extends AbstractTask {
+
+ private final Configuration configuration;
+
+ private final Map versionConstraints = new TreeMap<>();
+
+ private final Set constrainedVersions = new TreeSet<>();
+
+ private final List projectPaths = new ArrayList();
+
+ public ExtractVersionConstraints() {
+ DependencyHandler dependencies = getProject().getDependencies();
+ this.configuration = getProject().getConfigurations().create(getName());
+ dependencies.getComponents().all(this::processMetadataDetails);
+ }
+
+ public void enforcedPlatform(String projectPath) {
+ this.configuration.getDependencies().add(getProject().getDependencies().enforcedPlatform(
+ getProject().getDependencies().project(Collections.singletonMap("path", projectPath))));
+ this.projectPaths.add(projectPath);
+ }
+
+ @Internal
+ public Map getVersionConstraints() {
+ return Collections.unmodifiableMap(this.versionConstraints);
+ }
+
+ @Internal
+ public Set getConstrainedVersions() {
+ return this.constrainedVersions;
+ }
+
+ @TaskAction
+ void extractVersionConstraints() {
+ this.configuration.resolve();
+ for (String projectPath : this.projectPaths) {
+ for (DependencyConstraint constraint : getProject().project(projectPath).getConfigurations()
+ .getByName("apiElements").getAllDependencyConstraints()) {
+ this.versionConstraints.put(constraint.getGroup() + ":" + constraint.getName(),
+ constraint.getVersionConstraint().toString());
+ this.constrainedVersions.add(new ConstrainedVersion(constraint.getGroup(), constraint.getName(),
+ constraint.getVersionConstraint().toString()));
+ }
+ }
+ }
+
+ private void processMetadataDetails(ComponentMetadataDetails details) {
+ details.allVariants((variantMetadata) -> variantMetadata.withDependencyConstraints((dependencyConstraints) -> {
+ for (DependencyConstraintMetadata constraint : dependencyConstraints) {
+ this.versionConstraints.put(constraint.getGroup() + ":" + constraint.getName(),
+ constraint.getVersionConstraint().toString());
+ this.constrainedVersions.add(new ConstrainedVersion(constraint.getGroup(), constraint.getName(),
+ constraint.getVersionConstraint().toString()));
+ }
+ }));
+ }
+
+ public static final class ConstrainedVersion implements Comparable, Serializable {
+
+ private final String group;
+
+ private final String artifact;
+
+ private final String version;
+
+ private ConstrainedVersion(String group, String artifact, String version) {
+ this.group = group;
+ this.artifact = artifact;
+ this.version = version;
+ }
+
+ public String getGroup() {
+ return this.group;
+ }
+
+ public String getArtifact() {
+ return this.artifact;
+ }
+
+ public String getVersion() {
+ return this.version;
+ }
+
+ @Override
+ public int compareTo(ConstrainedVersion other) {
+ int groupComparison = this.group.compareTo(other.group);
+ if (groupComparison != 0) {
+ return groupComparison;
+ }
+ return this.artifact.compareTo(other.artifact);
+ }
+
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/AsciidocBuilder.java b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/AsciidocBuilder.java
similarity index 91%
rename from spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/AsciidocBuilder.java
rename to buildSrc/src/main/java/org/springframework/boot/build/context/properties/AsciidocBuilder.java
index e4cab45468d..305da3abcfe 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/AsciidocBuilder.java
+++ b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/AsciidocBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.boot.configurationdocs;
+package org.springframework.boot.build.context.properties;
/**
* Simple builder to help construct Asciidoc markup.
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/CompoundConfigurationTableEntry.java b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/CompoundConfigurationTableEntry.java
similarity index 77%
rename from spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/CompoundConfigurationTableEntry.java
rename to buildSrc/src/main/java/org/springframework/boot/build/context/properties/CompoundConfigurationTableEntry.java
index 603e902f5dc..f045b5d7308 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/CompoundConfigurationTableEntry.java
+++ b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/CompoundConfigurationTableEntry.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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,14 +14,12 @@
* limitations under the License.
*/
-package org.springframework.boot.configurationdocs;
+package org.springframework.boot.build.context.properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Stream;
-import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
-
/**
* Table entry regrouping a list of configuration properties sharing the same description.
*
@@ -39,8 +37,8 @@ class CompoundConfigurationTableEntry extends ConfigurationTableEntry {
this.configurationKeys = new TreeSet<>();
}
- void addConfigurationKeys(ConfigurationMetadataProperty... properties) {
- Stream.of(properties).map(ConfigurationMetadataProperty::getId).forEach(this.configurationKeys::add);
+ void addConfigurationKeys(ConfigurationProperty... properties) {
+ Stream.of(properties).map(ConfigurationProperty::getName).forEach(this.configurationKeys::add);
}
@Override
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/ConfigurationMetadataDocumentWriter.java b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationMetadataDocumentWriter.java
similarity index 77%
rename from spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/ConfigurationMetadataDocumentWriter.java
rename to buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationMetadataDocumentWriter.java
index 0269c91cf4d..f5661073221 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/ConfigurationMetadataDocumentWriter.java
+++ b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationMetadataDocumentWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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,10 +14,9 @@
* limitations under the License.
*/
-package org.springframework.boot.configurationdocs;
+package org.springframework.boot.build.context.properties;
import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -28,8 +27,7 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
-import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
-import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder;
+import org.gradle.api.file.FileCollection;
/**
* Write Asciidoc documents with configuration properties listings.
@@ -39,14 +37,13 @@ import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepos
*/
public class ConfigurationMetadataDocumentWriter {
- public void writeDocument(Path outputDirectory, DocumentOptions options, InputStream... metadata)
+ public void writeDocument(Path outputDirectory, DocumentOptions options, FileCollection metadataFiles)
throws IOException {
assertValidOutputDirectory(outputDirectory);
if (!Files.exists(outputDirectory)) {
Files.createDirectory(outputDirectory);
}
- assertMetadata(metadata);
- List tables = createConfigTables(getMetadataProperties(metadata), options);
+ List tables = createConfigTables(ConfigurationProperties.fromFiles(metadataFiles), options);
for (ConfigurationTable table : tables) {
writeConfigurationTable(table, outputDirectory);
}
@@ -61,24 +58,11 @@ public class ConfigurationMetadataDocumentWriter {
}
}
- private void assertMetadata(InputStream... metadata) {
- if (metadata == null || metadata.length < 1) {
- throw new IllegalArgumentException("missing input metadata");
- }
- }
-
- private Map getMetadataProperties(InputStream... metadata)
- throws IOException {
- ConfigurationMetadataRepositoryJsonBuilder builder = ConfigurationMetadataRepositoryJsonBuilder
- .create(metadata);
- return builder.build().getAllProperties();
- }
-
- private List createConfigTables(Map metadataProperties,
+ private List createConfigTables(Map metadataProperties,
DocumentOptions options) {
List tables = new ArrayList<>();
List unmappedKeys = metadataProperties.values().stream().filter((property) -> !property.isDeprecated())
- .map(ConfigurationMetadataProperty::getId).collect(Collectors.toList());
+ .map(ConfigurationProperty::getName).collect(Collectors.toList());
Map overrides = getOverrides(metadataProperties, unmappedKeys,
options);
options.getMetadataSections().forEach((id, keyPrefixes) -> tables
@@ -95,8 +79,7 @@ public class ConfigurationMetadataDocumentWriter {
}
private Map getOverrides(
- Map metadataProperties, List unmappedKeys,
- DocumentOptions options) {
+ Map metadataProperties, List unmappedKeys, DocumentOptions options) {
Map overrides = new HashMap<>();
options.getOverrides().forEach((keyPrefix, description) -> {
CompoundConfigurationTableEntry entry = new CompoundConfigurationTableEntry(keyPrefix, description);
@@ -111,7 +94,7 @@ public class ConfigurationMetadataDocumentWriter {
return overrides;
}
- private ConfigurationTable createConfigTable(Map metadataProperties,
+ private ConfigurationTable createConfigTable(Map metadataProperties,
List unmappedKeys, Map overrides, String id,
List keyPrefixes) {
ConfigurationTable table = new ConfigurationTable(id);
@@ -123,7 +106,7 @@ public class ConfigurationMetadataDocumentWriter {
List matchingKeys = unmappedKeys.stream()
.filter((key) -> keyPrefixes.stream().anyMatch(key::startsWith)).collect(Collectors.toList());
for (String matchingKey : matchingKeys) {
- ConfigurationMetadataProperty property = metadataProperties.get(matchingKey);
+ ConfigurationProperty property = metadataProperties.get(matchingKey);
table.addEntry(new SingleConfigurationTableEntry(property));
}
unmappedKeys.removeAll(matchingKeys);
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationProperties.java b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationProperties.java
new file mode 100644
index 00000000000..5fa43adee13
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationProperties.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.context.properties;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+import org.gradle.api.file.FileCollection;
+
+/**
+ * Configuration properties read from one or more
+ * {@code META-INF/spring-configuration-metadata.json} files.
+ *
+ * @author Andy Wilkinson
+ */
+final class ConfigurationProperties {
+
+ private static final Type MAP_TYPE = new MapTypeToken().getType();
+
+ private ConfigurationProperties() {
+
+ }
+
+ @SuppressWarnings("unchecked")
+ static Map fromFiles(FileCollection files) {
+ List configurationProperties = new ArrayList<>();
+ try {
+ Gson gson = new GsonBuilder().create();
+ for (File file : files) {
+ try (Reader reader = new FileReader(file)) {
+ Map json = gson.fromJson(reader, MAP_TYPE);
+ List> properties = (List>) json.get("properties");
+ for (Map property : properties) {
+ String name = (String) property.get("name");
+ String type = (String) property.get("type");
+ Object defaultValue = property.get("defaultValue");
+ String description = (String) property.get("description");
+ boolean deprecated = property.containsKey("deprecated");
+ configurationProperties
+ .add(new ConfigurationProperty(name, type, defaultValue, description, deprecated));
+ }
+ }
+ }
+ return configurationProperties.stream()
+ .collect(Collectors.toMap(ConfigurationProperty::getName, Function.identity()));
+ }
+ catch (IOException ex) {
+ throw new RuntimeException("Failed to load configuration metadata", ex);
+ }
+ }
+
+ private static final class MapTypeToken extends TypeToken> {
+
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationPropertiesPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationPropertiesPlugin.java
new file mode 100644
index 00000000000..81f51281fb4
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationPropertiesPlugin.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.context.properties;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.concurrent.Callable;
+import java.util.stream.Collectors;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.plugins.JavaPluginConvention;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.tasks.compile.JavaCompile;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link Plugin} for projects that define {@code @ConfigurationProperties}. When applied,
+ * the plugin reacts to the presence of the {@link JavaPlugin} by:
+ *
+ *
+ * Adding a dependency on the configuration properties annotation processor.
+ * Configure the additional metadata locations annotation processor compiler argument
+ * Defining an artifact for the resulting configuration property metadata so that it
+ * can be consumed by downstream projects.
+ *
+ *
+ * @author Andy Wilkinson
+ */
+public class ConfigurationPropertiesPlugin implements Plugin {
+
+ /**
+ * Name of the {@link Configuration} that holds the configuration property metadata
+ * artifact.
+ */
+ public static final String CONFIGURATION_PROPERTIES_METADATA_CONFIGURATION_NAME = "configurationPropertiesMetadata";
+
+ @Override
+ public void apply(Project project) {
+ project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
+ addConfigurationProcessorDependency(project);
+ configureAdditionalMetadataLocationsCompilerArgument(project);
+ addMetadataArtifact(project);
+ });
+ }
+
+ private void addConfigurationProcessorDependency(Project project) {
+ Configuration annotationProcessors = project.getConfigurations()
+ .getByName(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME);
+ annotationProcessors.getDependencies().add(project.getDependencies().project(Collections.singletonMap("path",
+ ":spring-boot-project:spring-boot-tools:spring-boot-configuration-processor")));
+ }
+
+ private void addMetadataArtifact(Project project) {
+ SourceSet mainSourceSet = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets()
+ .getByName(SourceSet.MAIN_SOURCE_SET_NAME);
+ project.getConfigurations().maybeCreate(CONFIGURATION_PROPERTIES_METADATA_CONFIGURATION_NAME);
+ project.afterEvaluate((evaluatedProject) -> evaluatedProject.getArtifacts().add(
+ CONFIGURATION_PROPERTIES_METADATA_CONFIGURATION_NAME,
+ evaluatedProject.provider((Callable) () -> new File(mainSourceSet.getJava().getOutputDir(),
+ "META-INF/spring-configuration-metadata.json")),
+ (artifact) -> artifact
+ .builtBy(evaluatedProject.getTasks().getByName(mainSourceSet.getClassesTaskName()))));
+ }
+
+ private void configureAdditionalMetadataLocationsCompilerArgument(Project project) {
+ JavaCompile compileJava = project.getTasks().withType(JavaCompile.class)
+ .getByName(JavaPlugin.COMPILE_JAVA_TASK_NAME);
+ SourceSet mainSourceSet = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets()
+ .getByName(SourceSet.MAIN_SOURCE_SET_NAME);
+ compileJava.getOptions().getCompilerArgs()
+ .add("-Aorg.springframework.boot.configurationprocessor.additionalMetadataLocations=" + StringUtils
+ .collectionToCommaDelimitedString(mainSourceSet.getResources().getSourceDirectories().getFiles()
+ .stream().map(project.getRootProject()::relativePath).collect(Collectors.toSet())));
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationProperty.java b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationProperty.java
new file mode 100644
index 00000000000..e66c236f478
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationProperty.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 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 org.springframework.boot.build.context.properties;
+
+/**
+ * A configuration property.
+ *
+ * @author Andy Wilkinson
+ */
+public class ConfigurationProperty {
+
+ private final String name;
+
+ private final String type;
+
+ private final Object defaultValue;
+
+ private final String description;
+
+ private final boolean deprecated;
+
+ ConfigurationProperty(String name, String type) {
+ this(name, type, null, null, false);
+ }
+
+ ConfigurationProperty(String name, String type, Object defaultValue, String description, boolean deprecated) {
+ this.name = name;
+ this.type = type;
+ this.defaultValue = defaultValue;
+ this.description = description;
+ this.deprecated = deprecated;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public String getType() {
+ return this.type;
+ }
+
+ public Object getDefaultValue() {
+ return this.defaultValue;
+ }
+
+ public String getDescription() {
+ return this.description;
+ }
+
+ public boolean isDeprecated() {
+ return this.deprecated;
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/ConfigurationTable.java b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationTable.java
similarity index 92%
rename from spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/ConfigurationTable.java
rename to buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationTable.java
index 83a36d9bb1d..533c0b8365d 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/ConfigurationTable.java
+++ b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationTable.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.boot.configurationdocs;
+package org.springframework.boot.build.context.properties;
import java.util.Arrays;
import java.util.Set;
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/ConfigurationTableEntry.java b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationTableEntry.java
similarity index 92%
rename from spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/ConfigurationTableEntry.java
rename to buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationTableEntry.java
index 8c81a960173..a0ebc3361b6 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/ConfigurationTableEntry.java
+++ b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/ConfigurationTableEntry.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.boot.configurationdocs;
+package org.springframework.boot.build.context.properties;
/**
* Abstract class for entries in {@link ConfigurationTable}.
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentConfigurationProperties.java b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentConfigurationProperties.java
new file mode 100644
index 00000000000..bb38138e2cc
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentConfigurationProperties.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.context.properties;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.gradle.api.Task;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.AbstractTask;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+
+import org.springframework.boot.build.context.properties.DocumentOptions.Builder;
+
+/**
+ * {@link Task} used to document auto-configuration classes.
+ *
+ * @author Andy Wilkinson
+ */
+public class DocumentConfigurationProperties extends AbstractTask {
+
+ private FileCollection configurationPropertyMetadata;
+
+ private File outputDir;
+
+ @InputFiles
+ public FileCollection getConfigurationPropertyMetadata() {
+ return this.configurationPropertyMetadata;
+ }
+
+ public void setConfigurationPropertyMetadata(FileCollection configurationPropertyMetadata) {
+ this.configurationPropertyMetadata = configurationPropertyMetadata;
+ }
+
+ @OutputDirectory
+ public File getOutputDir() {
+ return this.outputDir;
+ }
+
+ public void setOutputDir(File outputDir) {
+ this.outputDir = outputDir;
+ }
+
+ @TaskAction
+ void documentConfigurationProperties() throws IOException {
+ Builder builder = DocumentOptions.builder();
+ builder.addSection("core")
+ .withKeyPrefixes("debug", "trace", "logging", "spring.aop", "spring.application",
+ "spring.autoconfigure", "spring.banner", "spring.beaninfo", "spring.codec", "spring.config",
+ "spring.info", "spring.jmx", "spring.main", "spring.messages", "spring.pid", "spring.profiles",
+ "spring.quartz", "spring.reactor", "spring.task", "spring.mandatory-file-encoding", "info",
+ "spring.output.ansi.enabled")
+ .addSection("mail").withKeyPrefixes("spring.mail", "spring.sendgrid").addSection("cache")
+ .withKeyPrefixes("spring.cache").addSection("server").withKeyPrefixes("server").addSection("web")
+ .withKeyPrefixes("spring.hateoas", "spring.http", "spring.servlet", "spring.jersey", "spring.mvc",
+ "spring.resources", "spring.webflux")
+ .addSection("json").withKeyPrefixes("spring.jackson", "spring.gson").addSection("rsocket")
+ .withKeyPrefixes("spring.rsocket").addSection("templating")
+ .withKeyPrefixes("spring.freemarker", "spring.groovy", "spring.mustache", "spring.thymeleaf")
+ .addOverride("spring.groovy.template.configuration", "See GroovyMarkupConfigurer")
+ .addSection("security").withKeyPrefixes("spring.security", "spring.ldap", "spring.session")
+ .addSection("data-migration").withKeyPrefixes("spring.flyway", "spring.liquibase").addSection("data")
+ .withKeyPrefixes("spring.couchbase", "spring.elasticsearch", "spring.h2", "spring.influx",
+ "spring.mongodb", "spring.redis", "spring.dao", "spring.data", "spring.datasource",
+ "spring.jooq", "spring.jdbc", "spring.jpa")
+ .addOverride("spring.datasource.dbcp2", "Commons DBCP2 specific settings")
+ .addOverride("spring.datasource.tomcat", "Tomcat datasource specific settings")
+ .addOverride("spring.datasource.hikari", "Hikari specific settings").addSection("transaction")
+ .withKeyPrefixes("spring.jta", "spring.transaction").addSection("integration")
+ .withKeyPrefixes("spring.activemq", "spring.artemis", "spring.batch", "spring.integration",
+ "spring.jms", "spring.kafka", "spring.rabbitmq", "spring.hazelcast", "spring.webservices")
+ .addSection("actuator").withKeyPrefixes("management").addSection("devtools")
+ .withKeyPrefixes("spring.devtools").addSection("testing").withKeyPrefixes("spring.test");
+ DocumentOptions options = builder.build();
+ new ConfigurationMetadataDocumentWriter().writeDocument(this.outputDir.toPath(), options,
+ this.configurationPropertyMetadata);
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/DocumentOptions.java b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentOptions.java
similarity index 95%
rename from spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/DocumentOptions.java
rename to buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentOptions.java
index 6e96e8d94bb..53fc4adec3f 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/DocumentOptions.java
+++ b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentOptions.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.boot.configurationdocs;
+package org.springframework.boot.build.context.properties;
import java.util.Arrays;
import java.util.HashMap;
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/SingleConfigurationTableEntry.java b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/SingleConfigurationTableEntry.java
similarity index 88%
rename from spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/SingleConfigurationTableEntry.java
rename to buildSrc/src/main/java/org/springframework/boot/build/context/properties/SingleConfigurationTableEntry.java
index 17ac8bc32a2..a2692818433 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/SingleConfigurationTableEntry.java
+++ b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/SingleConfigurationTableEntry.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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,13 +14,11 @@
* limitations under the License.
*/
-package org.springframework.boot.configurationdocs;
+package org.springframework.boot.build.context.properties;
import java.util.Arrays;
import java.util.stream.Collectors;
-import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
-
/**
* Table entry containing a single configuration property.
*
@@ -32,8 +30,8 @@ class SingleConfigurationTableEntry extends ConfigurationTableEntry {
private final String defaultValue;
- SingleConfigurationTableEntry(ConfigurationMetadataProperty property) {
- this.key = property.getId();
+ SingleConfigurationTableEntry(ConfigurationProperty property) {
+ this.key = property.getName();
if (property.getType() != null && property.getType().startsWith("java.util.Map")) {
this.key += ".*";
}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/log4j2/ReproducibleLog4j2PluginsDatAction.java b/buildSrc/src/main/java/org/springframework/boot/build/log4j2/ReproducibleLog4j2PluginsDatAction.java
new file mode 100644
index 00000000000..0414fb1cbb7
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/log4j2/ReproducibleLog4j2PluginsDatAction.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.log4j2;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+import org.gradle.api.Action;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.tasks.TaskExecutionException;
+import org.gradle.api.tasks.compile.JavaCompile;
+
+/**
+ * An {@Action} to post-process a {@code Log4j2Plugins.dat} and re-order its content so
+ * that it is reproducible.
+ *
+ * @author Andy Wilkinson
+ */
+public class ReproducibleLog4j2PluginsDatAction implements Action {
+
+ @Override
+ public void execute(JavaCompile javaCompile) {
+ File datFile = new File(javaCompile.getDestinationDir(),
+ "META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat");
+ try {
+ postProcess(datFile);
+ }
+ catch (IOException ex) {
+ throw new TaskExecutionException(javaCompile, ex);
+ }
+ }
+
+ void postProcess(File datFile) throws IOException {
+ if (!datFile.isFile()) {
+ throw new InvalidUserDataException(
+ "META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat not found");
+ }
+ Map> categories = new TreeMap<>();
+ try (DataInputStream input = new DataInputStream(new FileInputStream(datFile))) {
+ int categoryCount = input.readInt();
+ for (int i = 0; i < categoryCount; i++) {
+ String categoryName = input.readUTF();
+ int pluginCount = input.readInt();
+ Map category = categories.computeIfAbsent(categoryName, (c) -> new TreeMap<>());
+ for (int j = 0; j < pluginCount; j++) {
+ Plugin plugin = new Plugin(input.readUTF(), input.readUTF(), input.readUTF(), input.readBoolean(),
+ input.readBoolean());
+ category.putIfAbsent(plugin.key, plugin);
+ }
+ }
+ }
+ try (DataOutputStream output = new DataOutputStream(new FileOutputStream(datFile))) {
+ output.writeInt(categories.size());
+ for (Entry> category : categories.entrySet()) {
+ output.writeUTF(category.getKey());
+ output.writeInt(category.getValue().size());
+ for (Plugin plugin : category.getValue().values()) {
+ output.writeUTF(plugin.key);
+ output.writeUTF(plugin.className);
+ output.writeUTF(plugin.name);
+ output.writeBoolean(plugin.printable);
+ output.writeBoolean(plugin.defer);
+ }
+ }
+ }
+ }
+
+ private static final class Plugin {
+
+ private final String key;
+
+ private final String className;
+
+ private final String name;
+
+ private final boolean printable;
+
+ private final boolean defer;
+
+ private Plugin(String key, String className, String name, boolean printable, boolean defer) {
+ this.key = key;
+ this.className = className;
+ this.name = name;
+ this.printable = printable;
+ this.defer = defer;
+ }
+
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/DocumentPluginGoals.java b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/DocumentPluginGoals.java
new file mode 100644
index 00000000000..2a16072dbb1
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/DocumentPluginGoals.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.mavenplugin;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Task;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+
+import org.springframework.boot.build.mavenplugin.PluginXmlParser.Mojo;
+import org.springframework.boot.build.mavenplugin.PluginXmlParser.Parameter;
+import org.springframework.boot.build.mavenplugin.PluginXmlParser.Plugin;
+
+/**
+ * A {@link Task} to document the plugin's goals.
+ *
+ * @author Andy Wilkinson
+ */
+public class DocumentPluginGoals extends DefaultTask {
+
+ private final PluginXmlParser parser = new PluginXmlParser();
+
+ private File pluginXml;
+
+ private File outputDir;
+
+ @OutputDirectory
+ public File getOutputDir() {
+ return this.outputDir;
+ }
+
+ public void setOutputDir(File outputDir) {
+ this.outputDir = outputDir;
+ }
+
+ @InputFile
+ public File getPluginXml() {
+ return this.pluginXml;
+ }
+
+ public void setPluginXml(File pluginXml) {
+ this.pluginXml = pluginXml;
+ }
+
+ @TaskAction
+ public void documentPluginGoals() throws IOException {
+ Plugin plugin = this.parser.parse(this.pluginXml);
+ writeOverview(plugin);
+ for (Mojo mojo : plugin.getMojos()) {
+ documentMojo(plugin, mojo);
+ }
+ }
+
+ private void writeOverview(Plugin plugin) throws IOException {
+ try (PrintWriter writer = new PrintWriter(new FileWriter(new File(this.outputDir, "overview.adoc")))) {
+ writer.println("[cols=\"1,3\"]");
+ writer.println("|===");
+ writer.println("| Goal | Description");
+ writer.println();
+ for (Mojo mojo : plugin.getMojos()) {
+ writer.printf("| <>%n", mojo.getGoal(), plugin.getGoalPrefix(), mojo.getGoal());
+ writer.printf("| %s%n", mojo.getDescription());
+ writer.println();
+ }
+ writer.println("|===");
+ }
+ }
+
+ private void documentMojo(Plugin plugin, Mojo mojo) throws IOException {
+ try (PrintWriter writer = new PrintWriter(new FileWriter(new File(this.outputDir, mojo.getGoal() + ".adoc")))) {
+ String sectionId = "goals-" + mojo.getGoal();
+ writer.println();
+ writer.println();
+ writer.printf("[[%s]]%n", sectionId);
+ writer.printf("== `%s:%s`%n", plugin.getGoalPrefix(), mojo.getGoal());
+ writer.printf("`%s:%s:%s`%n", plugin.getGroupId(), plugin.getArtifactId(), plugin.getVersion());
+ writer.println();
+ writer.println(mojo.getDescription());
+ List parameters = mojo.getParameters().stream().filter(Parameter::isEditable)
+ .collect(Collectors.toList());
+ List requiredParameters = parameters.stream().filter(Parameter::isRequired)
+ .collect(Collectors.toList());
+ String parametersSectionId = sectionId + "-parameters";
+ String detailsSectionId = parametersSectionId + "-details";
+ if (!requiredParameters.isEmpty()) {
+ writer.println();
+ writer.println();
+ writer.printf("[[%s-required]]%n", parametersSectionId);
+ writer.println("=== Required parameters");
+ writeParametersTable(writer, mojo.getGoal(), requiredParameters);
+ }
+ List optionalParameters = parameters.stream().filter((parameter) -> !parameter.isRequired())
+ .collect(Collectors.toList());
+ if (!optionalParameters.isEmpty()) {
+ writer.println();
+ writer.println();
+ writer.printf("[[%s-optional]]%n", parametersSectionId);
+ writer.println("=== Optional parameters");
+ writeParametersTable(writer, detailsSectionId, optionalParameters);
+ }
+ writer.println();
+ writer.println();
+ writer.printf("[[%s]]%n", detailsSectionId);
+ writer.println("=== Parameter details");
+ writeParameterDetails(writer, parameters, detailsSectionId);
+ }
+ }
+
+ private void writeParametersTable(PrintWriter writer, String detailsSectionId, List parameters) {
+ writer.println("[cols=\"3,2,3\"]");
+ writer.println("|===");
+ writer.println("| Name | Type | Default");
+ writer.println();
+ for (Parameter parameter : parameters) {
+ String name = parameter.getName();
+ writer.printf("| <<%s-%s,%s>>%n", detailsSectionId, name, name);
+ String type = parameter.getType();
+ if (type.lastIndexOf('.') >= 0) {
+ type = type.substring(type.lastIndexOf('.') + 1);
+ }
+ writer.printf("| `%s`%n", type);
+ String defaultValue = parameter.getDefaultValue();
+ if (defaultValue != null) {
+ writer.printf("| `%s`%n", defaultValue);
+ }
+ else {
+ writer.println("|");
+ }
+ writer.println();
+ }
+ writer.println("|===");
+ }
+
+ private void writeParameterDetails(PrintWriter writer, List parameters, String sectionId) {
+ for (Parameter parameter : parameters) {
+ String name = parameter.getName();
+ writer.println();
+ writer.println();
+ writer.printf("[[%s-%s]]%n", sectionId, name);
+ writer.printf("==== `%s`%n", name);
+ writer.println(parameter.getDescription());
+ writer.println();
+ writer.println("[cols=\"10h,90\"]");
+ writer.println("|===");
+ writer.println();
+ writeDetail(writer, "Name", name);
+ writeDetail(writer, "Type", parameter.getType());
+ writeOptionalDetail(writer, "Default value", parameter.getDefaultValue());
+ writeOptionalDetail(writer, "User property", parameter.getUserProperty());
+ writeOptionalDetail(writer, "Since", parameter.getSince());
+ writer.println("|===");
+ }
+ }
+
+ private void writeDetail(PrintWriter writer, String name, String value) {
+ writer.printf("| %s%n", name);
+ writer.printf("| `%s`%n", value);
+ writer.println();
+ }
+
+ private void writeOptionalDetail(PrintWriter writer, String name, String value) {
+ writer.printf("| %s%n", name);
+ if (value != null) {
+ writer.printf("| `%s`%n", value);
+ }
+ else {
+ writer.println("|");
+ }
+ writer.println();
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/MavenExec.java b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/MavenExec.java
new file mode 100644
index 00000000000..17694f11d14
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/MavenExec.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.mavenplugin;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.tasks.Internal;
+import org.gradle.api.tasks.JavaExec;
+import org.gradle.api.tasks.TaskExecutionException;
+import org.gradle.process.internal.ExecException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A custom {@link JavaExec} {@link Task task} for running Maven.
+ *
+ * @author Andy Wilkinson
+ */
+public class MavenExec extends JavaExec {
+
+ private Logger log = LoggerFactory.getLogger(MavenExec.class);
+
+ private File projectDir;
+
+ public MavenExec() throws IOException {
+ setClasspath(mavenConfiguration(getProject()));
+ args("--batch-mode");
+ setMain("org.apache.maven.cli.MavenCli");
+ }
+
+ public void setProjectDir(File projectDir) {
+ this.projectDir = projectDir;
+ getInputs().file(new File(projectDir, "pom.xml"));
+ }
+
+ @Override
+ public void exec() {
+ workingDir(this.projectDir);
+ systemProperty("maven.multiModuleProjectDirectory", this.projectDir.getAbsolutePath());
+ try {
+ Path logFile = Files.createTempFile(getName(), ".log");
+ try {
+ args("--log-file", logFile.toFile().getAbsolutePath());
+ super.exec();
+ if (this.log.isInfoEnabled()) {
+ Files.readAllLines(logFile).forEach(this.log::info);
+ }
+ }
+ catch (ExecException ex) {
+ System.out.println("Exec exception! Dumping log");
+ Files.readAllLines(logFile).forEach(System.out::println);
+ throw ex;
+ }
+ }
+ catch (IOException ex) {
+ throw new TaskExecutionException(this, ex);
+ }
+ }
+
+ private Configuration mavenConfiguration(Project project) {
+ Configuration existing = project.getConfigurations().findByName("maven");
+ if (existing != null) {
+ return existing;
+ }
+ return project.getConfigurations().create("maven", (maven) -> {
+ maven.getDependencies().add(project.getDependencies().create("org.apache.maven:maven-embedder:3.6.2"));
+ maven.getDependencies().add(project.getDependencies().create("org.apache.maven:maven-compat:3.6.2"));
+ maven.getDependencies().add(project.getDependencies().create("org.slf4j:slf4j-simple:1.7.5"));
+ maven.getDependencies().add(
+ project.getDependencies().create("org.apache.maven.resolver:maven-resolver-connector-basic:1.4.1"));
+ maven.getDependencies().add(
+ project.getDependencies().create("org.apache.maven.resolver:maven-resolver-transport-http:1.4.1"));
+ });
+ }
+
+ @Internal
+ public File getProjectDir() {
+ return this.projectDir;
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/MavenPluginPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/MavenPluginPlugin.java
new file mode 100644
index 00000000000..08874b11aaa
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/MavenPluginPlugin.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 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 org.springframework.boot.build.mavenplugin;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+
+import io.spring.javaformat.formatter.FileFormatter;
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.plugins.JavaLibraryPlugin;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.plugins.JavaPluginConvention;
+import org.gradle.api.publish.PublishingExtension;
+import org.gradle.api.publish.maven.MavenPublication;
+import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
+import org.gradle.api.tasks.Copy;
+import org.gradle.api.tasks.JavaExec;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.tasks.SourceSetContainer;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.TaskExecutionException;
+import org.gradle.api.tasks.bundling.Jar;
+import org.gradle.api.tasks.javadoc.Javadoc;
+import org.gradle.external.javadoc.StandardJavadocDocletOptions;
+
+import org.springframework.boot.build.DeployedPlugin;
+import org.springframework.boot.build.MavenRepositoryPlugin;
+import org.springframework.boot.build.test.IntegrationTestPlugin;
+
+/**
+ * Plugin for building Spring Boot's Maven Plugin.
+ *
+ * @author Andy Wilkinson
+ */
+public class MavenPluginPlugin implements Plugin {
+
+ @Override
+ public void apply(Project project) {
+ project.getPlugins().apply(JavaLibraryPlugin.class);
+ project.getPlugins().apply(MavenPublishPlugin.class);
+ project.getPlugins().apply(DeployedPlugin.class);
+ project.getPlugins().apply(MavenRepositoryPlugin.class);
+ project.getPlugins().apply(IntegrationTestPlugin.class);
+ Copy populateIntTestMavenRepository = project.getTasks().create("populateIntTestMavenRepository", Copy.class);
+ populateIntTestMavenRepository.setDestinationDir(project.getBuildDir());
+ populateIntTestMavenRepository.into("int-test-maven-repository", (copy) -> {
+ copy.from(project.getConfigurations().getByName(MavenRepositoryPlugin.MAVEN_REPOSITORY_CONFIGURATION_NAME));
+ copy.from(new File(project.getBuildDir(), "maven-repository"));
+ });
+ populateIntTestMavenRepository
+ .dependsOn(project.getTasks().getByName(MavenRepositoryPlugin.PUBLISH_TO_PROJECT_REPOSITORY_TASK_NAME));
+ configurePomPackaging(project);
+ MavenExec generateHelpMojo = configureMojoGenerationTasks(project);
+ MavenExec generatePluginDescriptor = configurePluginDescriptorGenerationTasks(project, generateHelpMojo);
+ DocumentPluginGoals documentPluginGoals = project.getTasks().create("documentPluginGoals",
+ DocumentPluginGoals.class);
+ documentPluginGoals.setPluginXml(generatePluginDescriptor.getOutputs().getFiles().getSingleFile());
+ documentPluginGoals.setOutputDir(new File(project.getBuildDir(), "docs/generated/goals/"));
+ documentPluginGoals.dependsOn(generatePluginDescriptor);
+ Jar jar = (Jar) project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME);
+ includeDescriptorInJar(jar, generatePluginDescriptor);
+ includeHelpMojoInJar(jar, generateHelpMojo);
+ PrepareMavenBinaries prepareMavenBinaries = project.getTasks().create("prepareMavenBinaries",
+ PrepareMavenBinaries.class);
+ prepareMavenBinaries.setOutputDir(new File(project.getBuildDir(), "maven-binaries"));
+ project.getTasks().getByName(IntegrationTestPlugin.INT_TEST_TASK_NAME).dependsOn(populateIntTestMavenRepository,
+ prepareMavenBinaries);
+ }
+
+ private void configurePomPackaging(Project project) {
+ PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
+ publishing.getPublications().withType(MavenPublication.class,
+ (mavenPublication) -> mavenPublication.pom((pom) -> pom.setPackaging("maven-plugin")));
+ }
+
+ private MavenExec configureMojoGenerationTasks(Project project) {
+ File helpMojoDir = new File(project.getBuildDir(), "help-mojo");
+ Copy helpMojoInputs = createCopyHelpMojoInputs(project, helpMojoDir);
+ MavenExec generateHelpMojo = createGenerateHelpMojo(project, helpMojoDir);
+ generateHelpMojo.dependsOn(helpMojoInputs);
+ return generateHelpMojo;
+ }
+
+ private Copy createCopyHelpMojoInputs(Project project, File mavenDir) {
+ Copy mojoInputs = project.getTasks().create("copyHelpMojoInputs", Copy.class);
+ mojoInputs.setDestinationDir(mavenDir);
+ mojoInputs.from(new File(project.getProjectDir(), "src/maven/resources/pom.xml"),
+ (sync) -> sync.filter((input) -> input.replace("{{version}}", project.getVersion().toString())));
+ return mojoInputs;
+ }
+
+ private MavenExec createGenerateHelpMojo(Project project, File mavenDir) {
+ MavenExec generateHelpMojo = project.getTasks().create("generateHelpMojo", MavenExec.class);
+ generateHelpMojo.setProjectDir(mavenDir);
+ generateHelpMojo.args("org.apache.maven.plugins:maven-plugin-plugin:3.6.0:helpmojo");
+ generateHelpMojo.getOutputs().dir(new File(mavenDir, "target/generated-sources/plugin"));
+ return generateHelpMojo;
+ }
+
+ private MavenExec configurePluginDescriptorGenerationTasks(Project project, MavenExec generateHelpMojo) {
+ File pluginDescriptorDir = new File(project.getBuildDir(), "plugin-descriptor");
+ SourceSetContainer sourceSets = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets();
+ SourceSet mainSourceSet = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
+ File generatedHelpMojoDir = new File(project.getBuildDir(), "generated/sources/helpMojo");
+ project.getTasks().withType(Javadoc.class,
+ (javadoc) -> ((StandardJavadocDocletOptions) javadoc.getOptions()).addMultilineStringsOption("tag")
+ .setValue(Arrays.asList("goal:X", "requiresProject:X", "threadSafe:X")));
+ FormatHelpMojoSource copyFormattedHelpMojoSource = project.getTasks().create("copyFormattedHelpMojoSource",
+ FormatHelpMojoSource.class);
+ copyFormattedHelpMojoSource.setGenerator(generateHelpMojo);
+ copyFormattedHelpMojoSource.setOutputDir(generatedHelpMojoDir);
+ mainSourceSet.getAllJava().srcDir(generatedHelpMojoDir);
+ project.getTasks().getByName(mainSourceSet.getCompileJavaTaskName()).dependsOn(copyFormattedHelpMojoSource);
+ Copy pluginDescriptorInputs = createCopyPluginDescriptorInputs(project, pluginDescriptorDir, mainSourceSet);
+ pluginDescriptorInputs.dependsOn(mainSourceSet.getClassesTaskName());
+ MavenExec generatePluginDescriptor = createGeneratePluginDescriptor(project, pluginDescriptorDir);
+ generatePluginDescriptor.dependsOn(pluginDescriptorInputs);
+ return generatePluginDescriptor;
+ }
+
+ private Copy createCopyPluginDescriptorInputs(Project project, File destination, SourceSet sourceSet) {
+ Copy pluginDescriptorInputs = project.getTasks().create("copyPluginDescriptorInputs", Copy.class);
+ pluginDescriptorInputs.setDestinationDir(destination);
+ pluginDescriptorInputs.from(new File(project.getProjectDir(), "src/maven/resources/pom.xml"),
+ (sync) -> sync.filter((input) -> input.replace("{{version}}", project.getVersion().toString())));
+ pluginDescriptorInputs.from(sourceSet.getOutput().getClassesDirs(), (sync) -> sync.into("target/classes"));
+ pluginDescriptorInputs.from(sourceSet.getAllJava().getSrcDirs(), (sync) -> sync.into("src/main/java"));
+ return pluginDescriptorInputs;
+ }
+
+ private MavenExec createGeneratePluginDescriptor(Project project, File mavenDir) {
+ MavenExec generatePluginDescriptor = project.getTasks().create("generatePluginDescriptor", MavenExec.class);
+ generatePluginDescriptor.args("org.apache.maven.plugins:maven-plugin-plugin:3.6.0:descriptor");
+ generatePluginDescriptor.getOutputs().file(new File(mavenDir, "target/classes/META-INF/maven/plugin.xml"));
+ generatePluginDescriptor.getInputs().dir(new File(mavenDir, "target/classes/org"));
+ generatePluginDescriptor.setProjectDir(mavenDir);
+ return generatePluginDescriptor;
+ }
+
+ private void includeDescriptorInJar(Jar jar, JavaExec generatePluginDescriptor) {
+ jar.from(generatePluginDescriptor, (copy) -> copy.into("META-INF/maven/"));
+ jar.dependsOn(generatePluginDescriptor);
+ }
+
+ private void includeHelpMojoInJar(Jar jar, JavaExec generateHelpMojo) {
+ jar.from(generateHelpMojo);
+ jar.dependsOn(generateHelpMojo);
+ }
+
+ public static class FormatHelpMojoSource extends DefaultTask {
+
+ private Task generator;
+
+ private File outputDir;
+
+ void setGenerator(Task generator) {
+ this.generator = generator;
+ getInputs().files(this.generator);
+ }
+
+ @OutputDirectory
+ public File getOutputDir() {
+ return this.outputDir;
+ }
+
+ void setOutputDir(File outputDir) {
+ this.outputDir = outputDir;
+ }
+
+ @TaskAction
+ void syncAndFormat() {
+ FileFormatter fileFormatter = new FileFormatter();
+ for (File output : this.generator.getOutputs().getFiles()) {
+ fileFormatter.formatFiles(getProject().fileTree(output), StandardCharsets.UTF_8).forEach((fileEdit) -> {
+ Path relativePath = output.toPath().relativize(fileEdit.getFile().toPath());
+ Path outputLocation = this.outputDir.toPath().resolve(relativePath);
+ try {
+ Files.createDirectories(outputLocation.getParent());
+ Files.write(outputLocation, fileEdit.getFormattedContent().getBytes(StandardCharsets.UTF_8));
+ }
+ catch (Exception ex) {
+ throw new TaskExecutionException(this, ex);
+ }
+ });
+ }
+ }
+
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PluginXmlParser.java b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PluginXmlParser.java
new file mode 100644
index 00000000000..6269923df2a
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PluginXmlParser.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright 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 org.springframework.boot.build.mavenplugin;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * A parser for a Maven plugin's {@code plugin.xml} file.
+ *
+ * @author Andy Wilkinson
+ */
+class PluginXmlParser {
+
+ private final XPath xpath;
+
+ PluginXmlParser() {
+ this.xpath = XPathFactory.newInstance().newXPath();
+ }
+
+ Plugin parse(File pluginXml) {
+ try {
+ Node root = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(pluginXml);
+ List mojos = parseMojos(root);
+ return new Plugin(textAt("//plugin/groupId", root), textAt("//plugin/artifactId", root),
+ textAt("//plugin/version", root), textAt("//plugin/goalPrefix", root), mojos);
+ }
+ catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private String textAt(String path, Node source) throws XPathExpressionException {
+ String text = this.xpath.evaluate(path + "/text()", source);
+ return (text.length() == 0) ? null : text;
+ }
+
+ private List parseMojos(Node plugin) throws XPathExpressionException {
+ List mojos = new ArrayList();
+ for (Node mojoNode : nodesAt("//plugin/mojos/mojo", plugin)) {
+ mojos.add(new Mojo(textAt("goal", mojoNode), format(textAt("description", mojoNode)),
+ parseParameters(mojoNode)));
+ }
+ return mojos;
+ }
+
+ private Iterable nodesAt(String path, Node source) throws XPathExpressionException {
+ return IterableNodeList.of((NodeList) this.xpath.evaluate(path, source, XPathConstants.NODESET));
+ }
+
+ private List parseParameters(Node mojoNode) throws XPathExpressionException {
+ Map defaultValues = new HashMap<>();
+ Map userProperties = new HashMap<>();
+ for (Node parameterConfigurationNode : nodesAt("configuration/*", mojoNode)) {
+ String userProperty = parameterConfigurationNode.getTextContent();
+ if (userProperty != null && userProperty.length() > 0) {
+ userProperties.put(parameterConfigurationNode.getNodeName(),
+ userProperty.replace("${", "`").replace("}", "`"));
+ }
+ Node defaultValueAttribute = parameterConfigurationNode.getAttributes().getNamedItem("default-value");
+ if (defaultValueAttribute != null && defaultValueAttribute.getTextContent().length() > 0) {
+ defaultValues.put(parameterConfigurationNode.getNodeName(), defaultValueAttribute.getTextContent());
+ }
+ }
+ List parameters = new ArrayList<>();
+ for (Node parameterNode : nodesAt("parameters/parameter", mojoNode)) {
+ parameters.add(parseParameter(parameterNode, defaultValues, userProperties));
+ }
+ return parameters;
+ }
+
+ private Parameter parseParameter(Node parameterNode, Map defaultValues,
+ Map userProperties) throws XPathExpressionException {
+ Parameter parameter = new Parameter(textAt("name", parameterNode), textAt("type", parameterNode),
+ booleanAt("required", parameterNode), booleanAt("editable", parameterNode),
+ format(textAt("description", parameterNode)), defaultValues.get(textAt("name", parameterNode)),
+ userProperties.get(textAt("name", parameterNode)), textAt("since", parameterNode));
+ return parameter;
+ }
+
+ private boolean booleanAt(String path, Node node) throws XPathExpressionException {
+ return Boolean.valueOf(textAt(path, node));
+ }
+
+ private String format(String input) {
+ return input.replace("", "`").replace("
", "`").replace("<", "<").replace(">", ">")
+ .replace(" ", " ").replace("\n", " ").replace(""", "\"").replaceAll("\\{@code (.*?)\\}", "`$1`")
+ .replaceAll("\\{@link (.*?)\\}", "`$1`").replaceAll("\\{@literal (.*?)\\}", "`$1`")
+ .replaceAll("(.*?) ", "\\$1[\\$2]");
+ }
+
+ private static final class IterableNodeList implements Iterable {
+
+ private final NodeList nodeList;
+
+ private IterableNodeList(NodeList nodeList) {
+ this.nodeList = nodeList;
+ }
+
+ private static Iterable of(NodeList nodeList) {
+ return new IterableNodeList(nodeList);
+ }
+
+ @Override
+ public Iterator iterator() {
+
+ return new Iterator() {
+
+ private int index = 0;
+
+ @Override
+ public boolean hasNext() {
+ return this.index < IterableNodeList.this.nodeList.getLength();
+ }
+
+ @Override
+ public Node next() {
+ return IterableNodeList.this.nodeList.item(this.index++);
+ }
+
+ };
+ }
+
+ }
+
+ static final class Plugin {
+
+ private final String groupId;
+
+ private final String artifactId;
+
+ private final String version;
+
+ private final String goalPrefix;
+
+ private final List mojos;
+
+ private Plugin(String groupId, String artifactId, String version, String goalPrefix, List mojos) {
+ this.groupId = groupId;
+ this.artifactId = artifactId;
+ this.version = version;
+ this.goalPrefix = goalPrefix;
+ this.mojos = mojos;
+ }
+
+ String getGroupId() {
+ return this.groupId;
+ }
+
+ String getArtifactId() {
+ return this.artifactId;
+ }
+
+ String getVersion() {
+ return this.version;
+ }
+
+ String getGoalPrefix() {
+ return this.goalPrefix;
+ }
+
+ List getMojos() {
+ return this.mojos;
+ }
+
+ }
+
+ static final class Mojo {
+
+ private final String goal;
+
+ private final String description;
+
+ private final List parameters;
+
+ private Mojo(String goal, String description, List parameters) {
+ this.goal = goal;
+ this.description = description;
+ this.parameters = parameters;
+ }
+
+ String getGoal() {
+ return this.goal;
+ }
+
+ String getDescription() {
+ return this.description;
+ }
+
+ List getParameters() {
+ return this.parameters;
+ }
+
+ }
+
+ static final class Parameter {
+
+ private final String name;
+
+ private final String type;
+
+ private final boolean required;
+
+ private final boolean editable;
+
+ private final String description;
+
+ private final String defaultValue;
+
+ private final String userProperty;
+
+ private final String since;
+
+ private Parameter(String name, String type, boolean required, boolean editable, String description,
+ String defaultValue, String userProperty, String since) {
+ this.name = name;
+ this.type = type;
+ this.required = required;
+ this.editable = editable;
+ this.description = description;
+ this.defaultValue = defaultValue;
+ this.userProperty = userProperty;
+ this.since = since;
+ }
+
+ String getName() {
+ return this.name;
+ }
+
+ String getType() {
+ return this.type;
+ }
+
+ boolean isRequired() {
+ return this.required;
+ }
+
+ boolean isEditable() {
+ return this.editable;
+ }
+
+ String getDescription() {
+ return this.description;
+ }
+
+ String getDefaultValue() {
+ return this.defaultValue;
+ }
+
+ String getUserProperty() {
+ return this.userProperty;
+ }
+
+ String getSince() {
+ return this.since;
+ }
+
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PrepareMavenBinaries.java b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PrepareMavenBinaries.java
new file mode 100644
index 00000000000..e4f18ece6b4
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/mavenplugin/PrepareMavenBinaries.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 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 org.springframework.boot.build.mavenplugin;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+
+/**
+ * {@link Task} to make Maven binaries available for integration testing.
+ *
+ * @author Andy Wilkinson
+ */
+public class PrepareMavenBinaries extends DefaultTask {
+
+ private Set versions = new LinkedHashSet<>();
+
+ private File outputDir;
+
+ @OutputDirectory
+ public File getOutputDir() {
+ return this.outputDir;
+ }
+
+ public void setOutputDir(File outputDir) {
+ this.outputDir = outputDir;
+ }
+
+ @Input
+ public Set getVersions() {
+ return this.versions;
+ }
+
+ public void versions(String... versions) {
+ this.versions.addAll(Arrays.asList(versions));
+ }
+
+ @TaskAction
+ public void prepareBinaries() {
+ for (String version : this.versions) {
+ Configuration configuration = getProject().getConfigurations().detachedConfiguration(
+ getProject().getDependencies().create("org.apache.maven:apache-maven:" + version + ":bin@zip"));
+ getProject().copy(
+ (copy) -> copy.into(this.outputDir).from(getProject().zipTree(configuration.getSingleFile())));
+ }
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/optional/OptionalDependenciesPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/optional/OptionalDependenciesPlugin.java
new file mode 100644
index 00000000000..0f622fcee85
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/optional/OptionalDependenciesPlugin.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.optional;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.attributes.Usage;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.plugins.JavaPluginConvention;
+import org.gradle.api.tasks.SourceSetContainer;
+import org.gradle.api.tasks.javadoc.Javadoc;
+import org.gradle.plugins.ide.eclipse.EclipsePlugin;
+import org.gradle.plugins.ide.eclipse.model.EclipseModel;
+
+/**
+ * A {@code Plugin} that adds support for Maven-style optional dependencies. Creates a new
+ * {@code optional} configuration. The {@code optional} configuration is part of the
+ * project's compile and runtime classpath's but does not affect the classpath of
+ * dependent projects.
+ *
+ * @author Andy Wilkinson
+ */
+public class OptionalDependenciesPlugin implements Plugin {
+
+ /**
+ * Name of the {@code optional} configuration.
+ */
+ public static final String OPTIONAL_CONFIGURATION_NAME = "optional";
+
+ @Override
+ public void apply(Project project) {
+ Configuration optional = project.getConfigurations().create("optional");
+ optional.attributes((attributes) -> attributes.attribute(Usage.USAGE_ATTRIBUTE,
+ project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME)));
+ project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
+ SourceSetContainer sourceSets = project.getConvention().getPlugin(JavaPluginConvention.class)
+ .getSourceSets();
+ sourceSets.all((sourceSet) -> {
+ sourceSet.setCompileClasspath(sourceSet.getCompileClasspath().plus(optional));
+ sourceSet.setRuntimeClasspath(sourceSet.getRuntimeClasspath().plus(optional));
+ });
+ project.getTasks().withType(Javadoc.class)
+ .all((javadoc) -> javadoc.setClasspath(javadoc.getClasspath().plus(optional)));
+ });
+ project.getPlugins().withType(EclipsePlugin.class,
+ (eclipePlugin) -> project.getExtensions().getByType(EclipseModel.class)
+ .classpath((classpath) -> classpath.getPlusConfigurations().add(optional)));
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/starters/DocumentStarters.java b/buildSrc/src/main/java/org/springframework/boot/build/starters/DocumentStarters.java
new file mode 100644
index 00000000000..a5cdf124d47
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/starters/DocumentStarters.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 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 org.springframework.boot.build.starters;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.AbstractTask;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link Task} to document all starter projects.
+ *
+ * @author Andy Wilkinson
+ */
+public class DocumentStarters extends AbstractTask {
+
+ private final Configuration starters;
+
+ private File outputDir;
+
+ public DocumentStarters() {
+ this.starters = getProject().getConfigurations().create("starters");
+ getProject().getGradle().projectsEvaluated((gradle) -> {
+ gradle.allprojects((project) -> {
+ if (project.getPlugins().hasPlugin(StarterPlugin.class)) {
+ Map dependency = new HashMap<>();
+ dependency.put("path", project.getPath());
+ dependency.put("configuration", "starterMetadata");
+ this.starters.getDependencies().add(project.getDependencies().project(dependency));
+ }
+ });
+ });
+ }
+
+ @OutputDirectory
+ public File getOutputDir() {
+ return this.outputDir;
+ }
+
+ public void setOutputDir(File outputDir) {
+ this.outputDir = outputDir;
+ }
+
+ @InputFiles
+ public FileCollection getStarters() {
+ return this.starters;
+ }
+
+ @TaskAction
+ void documentStarters() {
+ Set starters = this.starters.getFiles().stream().map(this::loadStarter)
+ .collect(Collectors.toCollection(TreeSet::new));
+ writeTable("application-starters", starters.stream().filter(Starter::isApplication));
+ writeTable("production-starters", starters.stream().filter(Starter::isProduction));
+ writeTable("technical-starters", starters.stream().filter(Starter::isTechnical));
+ }
+
+ private Starter loadStarter(File metadata) {
+ Properties properties = new Properties();
+ try (FileReader reader = new FileReader(metadata)) {
+ properties.load(reader);
+ return new Starter(properties.getProperty("name"), properties.getProperty("description"),
+ StringUtils.commaDelimitedListToSet(properties.getProperty("dependencies")));
+ }
+ catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private void writeTable(String name, Stream starters) {
+ File output = new File(this.outputDir, name + ".adoc");
+ output.getParentFile().mkdirs();
+ try (PrintWriter writer = new PrintWriter(new FileWriter(output))) {
+ writer.println("|===");
+ writer.println("| Name | Description");
+ starters.forEach((starter) -> {
+ writer.println();
+ writer.printf("| [[%s]]`%s`%n", starter.name, starter.name);
+ writer.printf("| %s%n", postProcessDescription(starter.description));
+ });
+ writer.println("|===");
+ }
+ catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private String postProcessDescription(String description) {
+ return addStarterCrossLinks(description);
+ }
+
+ private String addStarterCrossLinks(String input) {
+ return input.replaceAll("(spring-boot-starter[A-Za-z-]*)", "<<$1,`$1`>>");
+ }
+
+ private static final class Starter implements Comparable {
+
+ private final String name;
+
+ private final String description;
+
+ private final Set dependencies;
+
+ private Starter(String name, String description, Set dependencies) {
+ this.name = name;
+ this.description = description;
+ this.dependencies = dependencies;
+ }
+
+ private boolean isProduction() {
+ return this.name.equals("spring-boot-starter-actuator");
+ }
+
+ private boolean isTechnical() {
+ return !Arrays.asList("spring-boot-starter", "spring-boot-starter-test").contains(this.name)
+ && !isProduction() && !this.dependencies.contains("spring-boot-starter");
+ }
+
+ private boolean isApplication() {
+ return !isProduction() && !isTechnical();
+ }
+
+ @Override
+ public int compareTo(Starter other) {
+ return this.name.compareTo(other.name);
+ }
+
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/starters/StarterMetadata.java b/buildSrc/src/main/java/org/springframework/boot/build/starters/StarterMetadata.java
new file mode 100644
index 00000000000..75e09c12738
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/starters/StarterMetadata.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.starters;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.concurrent.Callable;
+import java.util.stream.Collectors;
+
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ResolvedArtifact;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.AbstractTask;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.TaskAction;
+
+/**
+ * A {@link Task} for generating metadata that describes a starter.
+ *
+ * @author Andy Wilkinson
+ */
+public class StarterMetadata extends AbstractTask {
+
+ private Configuration dependencies;
+
+ private File destination;
+
+ public StarterMetadata() {
+ getInputs().property("name", (Callable) () -> getProject().getName());
+ getInputs().property("description", (Callable) () -> getProject().getDescription());
+ }
+
+ @InputFiles
+ public FileCollection getDependencies() {
+ return this.dependencies;
+ }
+
+ public void setDependencies(Configuration dependencies) {
+ this.dependencies = dependencies;
+ }
+
+ @OutputFile
+ public File getDestination() {
+ return this.destination;
+ }
+
+ public void setDestination(File destination) {
+ this.destination = destination;
+ }
+
+ @TaskAction
+ void generateMetadata() throws IOException {
+ Properties properties = new Properties();
+ properties.setProperty("name", getProject().getName());
+ properties.setProperty("description", getProject().getDescription());
+ properties.setProperty("dependencies", String.join(",", this.dependencies.getResolvedConfiguration()
+ .getResolvedArtifacts().stream().map(ResolvedArtifact::getName).collect(Collectors.toSet())));
+ this.destination.getParentFile().mkdirs();
+ try (FileWriter writer = new FileWriter(this.destination)) {
+ properties.store(writer, null);
+ }
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/starters/StarterPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/starters/StarterPlugin.java
new file mode 100644
index 00000000000..3ad6a3f714c
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/starters/StarterPlugin.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 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 org.springframework.boot.build.starters;
+
+import java.io.File;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.plugins.JavaBasePlugin;
+import org.gradle.api.plugins.JavaLibraryPlugin;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.plugins.PluginContainer;
+
+import org.springframework.boot.build.ConventionsPlugin;
+import org.springframework.boot.build.DeployedPlugin;
+import org.springframework.boot.build.classpath.CheckClasspathForConflicts;
+import org.springframework.boot.build.classpath.CheckClasspathForProhibitedDependencies;
+import org.springframework.util.StringUtils;
+
+/**
+ * A {@link Plugin} for a starter project.
+ *
+ * @author Andy Wilkinson
+ */
+public class StarterPlugin implements Plugin {
+
+ @Override
+ public void apply(Project project) {
+ PluginContainer plugins = project.getPlugins();
+ plugins.apply(DeployedPlugin.class);
+ plugins.apply(JavaLibraryPlugin.class);
+ plugins.apply(ConventionsPlugin.class);
+ StarterMetadata starterMetadata = project.getTasks().create("starterMetadata", StarterMetadata.class);
+ ConfigurationContainer configurations = project.getConfigurations();
+ Configuration runtimeClasspath = configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
+ starterMetadata.setDependencies(runtimeClasspath);
+ File destination = new File(project.getBuildDir(), "starter-metadata.properties");
+ starterMetadata.setDestination(destination);
+ configurations.create("starterMetadata");
+ project.getArtifacts().add("starterMetadata", project.provider(starterMetadata::getDestination),
+ (artifact) -> artifact.builtBy(starterMetadata));
+ createClasspathConflictsCheck(runtimeClasspath, project);
+ createProhibitedDependenciesCheck(runtimeClasspath, project);
+ }
+
+ private void createClasspathConflictsCheck(Configuration classpath, Project project) {
+ CheckClasspathForConflicts checkClasspathForConflicts = project.getTasks().create(
+ "check" + StringUtils.capitalize(classpath.getName() + "ForConflicts"),
+ CheckClasspathForConflicts.class);
+ checkClasspathForConflicts.setClasspath(classpath);
+ project.getTasks().getByName(JavaBasePlugin.CHECK_TASK_NAME).dependsOn(checkClasspathForConflicts);
+ }
+
+ private void createProhibitedDependenciesCheck(Configuration classpath, Project project) {
+ CheckClasspathForProhibitedDependencies checkClasspathForProhibitedDependencies = project.getTasks().create(
+ "check" + StringUtils.capitalize(classpath.getName() + "ForProhibitedDependencies"),
+ CheckClasspathForProhibitedDependencies.class);
+ checkClasspathForProhibitedDependencies.setClasspath(classpath);
+ project.getTasks().getByName(JavaBasePlugin.CHECK_TASK_NAME).dependsOn(checkClasspathForProhibitedDependencies);
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/test/IntegrationTestPlugin.java b/buildSrc/src/main/java/org/springframework/boot/build/test/IntegrationTestPlugin.java
new file mode 100644
index 00000000000..58ac2fb33f1
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/test/IntegrationTestPlugin.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.test;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.plugins.JavaPluginConvention;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.tasks.SourceSetContainer;
+import org.gradle.api.tasks.testing.Test;
+import org.gradle.language.base.plugins.LifecycleBasePlugin;
+import org.gradle.plugins.ide.eclipse.EclipsePlugin;
+import org.gradle.plugins.ide.eclipse.model.EclipseModel;
+
+/**
+ * A {@Plugin} to configure integration testing support in a {@link Project}.
+ *
+ * @author Andy Wilkinson
+ */
+public class IntegrationTestPlugin implements Plugin {
+
+ /**
+ * Name of the {@code intTest} task.
+ */
+ public static String INT_TEST_TASK_NAME = "intTest";
+
+ /**
+ * Name of the {@code intTest} source set.
+ */
+ public static String INT_TEST_SOURCE_SET_NAME = "intTest";
+
+ @Override
+ public void apply(Project project) {
+ project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> this.configureIntegrationTesting(project));
+ }
+
+ private void configureIntegrationTesting(Project project) {
+ SourceSet intTestSourceSet = createSourceSet(project);
+ Test intTest = createTestTask(project, intTestSourceSet);
+ project.getTasks().getByName(LifecycleBasePlugin.CHECK_TASK_NAME).dependsOn(intTest);
+ project.getPlugins().withType(EclipsePlugin.class, (eclipsePlugin) -> {
+ EclipseModel eclipse = project.getExtensions().getByType(EclipseModel.class);
+ eclipse.classpath((classpath) -> classpath.getPlusConfigurations().add(
+ project.getConfigurations().getByName(intTestSourceSet.getRuntimeClasspathConfigurationName())));
+ });
+ }
+
+ private SourceSet createSourceSet(Project project) {
+ SourceSetContainer sourceSets = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets();
+ SourceSet intTestSourceSet = sourceSets.create(INT_TEST_SOURCE_SET_NAME);
+ SourceSet main = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
+ intTestSourceSet.setCompileClasspath(intTestSourceSet.getCompileClasspath().plus(main.getOutput()));
+ intTestSourceSet.setRuntimeClasspath(intTestSourceSet.getRuntimeClasspath().plus(main.getOutput()));
+ return intTestSourceSet;
+ }
+
+ private Test createTestTask(Project project, SourceSet intTestSourceSet) {
+ Test intTest = project.getTasks().create(INT_TEST_TASK_NAME, Test.class);
+ intTest.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
+ intTest.setDescription("Runs integration tests.");
+ intTest.setTestClassesDirs(intTestSourceSet.getOutput().getClassesDirs());
+ intTest.setClasspath(intTestSourceSet.getRuntimeClasspath());
+ intTest.shouldRunAfter(JavaPlugin.TEST_TASK_NAME);
+ return intTest;
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/test/autoconfigure/DocumentTestSlices.java b/buildSrc/src/main/java/org/springframework/boot/build/test/autoconfigure/DocumentTestSlices.java
new file mode 100644
index 00000000000..6643b9fcf19
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/test/autoconfigure/DocumentTestSlices.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.test.autoconfigure;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.gradle.api.Task;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.AbstractTask;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.TaskAction;
+
+import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link Task} used to document test slices.
+ *
+ * @author Andy Wilkinson
+ */
+public class DocumentTestSlices extends AbstractTask {
+
+ private FileCollection testSlices;
+
+ private File outputFile;
+
+ @InputFiles
+ public FileCollection getTestSlices() {
+ return this.testSlices;
+ }
+
+ public void setTestSlices(FileCollection testSlices) {
+ this.testSlices = testSlices;
+ }
+
+ @OutputFile
+ public File getOutputFile() {
+ return this.outputFile;
+ }
+
+ public void setOutputFile(File outputFile) {
+ this.outputFile = outputFile;
+ }
+
+ @TaskAction
+ void documentTestSlices() throws IOException {
+ Set testSlices = readTestSlices();
+ writeTable(testSlices);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Set readTestSlices() throws IOException {
+ Set testSlices = new TreeSet<>();
+ for (File metadataFile : this.testSlices) {
+ Properties metadata = new Properties();
+ try (Reader reader = new FileReader(metadataFile)) {
+ metadata.load(reader);
+ }
+ for (String name : Collections.list((Enumeration) metadata.propertyNames())) {
+ testSlices.add(new TestSlice(name,
+ new TreeSet<>(StringUtils.commaDelimitedListToSet(metadata.getProperty(name)))));
+ }
+ }
+ return testSlices;
+ }
+
+ private void writeTable(Set testSlices) throws IOException {
+ this.outputFile.getParentFile().mkdirs();
+ try (PrintWriter writer = new PrintWriter(new FileWriter(this.outputFile))) {
+ writer.println("[cols=\"d,a\"]");
+ writer.println("|===");
+ writer.println("| Test slice | Imported auto-configuration");
+ for (TestSlice testSlice : testSlices) {
+ writer.println();
+ writer.printf("| `@%s`%n", testSlice.className);
+ writer.println("| ");
+ for (String importedAutoConfiguration : testSlice.importedAutoConfigurations) {
+ writer.printf("`%s`%n", importedAutoConfiguration);
+ }
+ }
+ writer.println("|===");
+ }
+ }
+
+ private static final class TestSlice implements Comparable {
+
+ private final String className;
+
+ private final SortedSet importedAutoConfigurations;
+
+ private TestSlice(String className, SortedSet importedAutoConfigurations) {
+ this.className = ClassUtils.getShortName(className);
+ this.importedAutoConfigurations = importedAutoConfigurations;
+ }
+
+ @Override
+ public int compareTo(TestSlice other) {
+ return this.className.compareTo(other.className);
+ }
+
+ }
+
+}
diff --git a/buildSrc/src/main/java/org/springframework/boot/build/test/autoconfigure/TestSliceMetadata.java b/buildSrc/src/main/java/org/springframework/boot/build/test/autoconfigure/TestSliceMetadata.java
new file mode 100644
index 00000000000..1a1c5ab9de1
--- /dev/null
+++ b/buildSrc/src/main/java/org/springframework/boot/build/test/autoconfigure/TestSliceMetadata.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.test.autoconfigure;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Properties;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.Callable;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.AbstractTask;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.tasks.TaskAction;
+
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.core.type.classreading.MetadataReader;
+import org.springframework.core.type.classreading.MetadataReaderFactory;
+import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
+import org.springframework.util.StringUtils;
+
+/**
+ * A {@link Task} for generating metadata describing a project's test slices.
+ *
+ * @author Andy Wilkinson
+ */
+public class TestSliceMetadata extends AbstractTask {
+
+ private SourceSet sourceSet;
+
+ private File outputFile;
+
+ public TestSliceMetadata() {
+ getInputs().dir((Callable) () -> this.sourceSet.getOutput().getResourcesDir());
+ getInputs().files((Callable) () -> this.sourceSet.getOutput().getClassesDirs());
+ }
+
+ public void setSourceSet(SourceSet sourceSet) {
+ this.sourceSet = sourceSet;
+ }
+
+ @OutputFile
+ public File getOutputFile() {
+ return this.outputFile;
+ }
+
+ public void setOutputFile(File outputFile) {
+ this.outputFile = outputFile;
+ Configuration testSliceMetadata = getProject().getConfigurations().maybeCreate("testSliceMetadata");
+ getProject().getArtifacts().add(testSliceMetadata.getName(),
+ getProject().provider((Callable) this::getOutputFile), (artifact) -> artifact.builtBy(this));
+ }
+
+ @TaskAction
+ void documentTestSlices() throws IOException {
+ Properties testSlices = readTestSlices();
+ getOutputFile().getParentFile().mkdirs();
+ try (FileWriter writer = new FileWriter(getOutputFile())) {
+ testSlices.store(writer, null);
+ }
+ }
+
+ private Properties readTestSlices() throws IOException {
+ Properties testSlices = new Properties();
+ try (URLClassLoader classLoader = new URLClassLoader(
+ StreamSupport.stream(this.sourceSet.getRuntimeClasspath().spliterator(), false).map(this::toURL)
+ .toArray(URL[]::new))) {
+ MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(classLoader);
+ Properties springFactories = readSpringFactories(
+ new File(this.sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories"));
+ for (File classesDir : this.sourceSet.getOutput().getClassesDirs()) {
+ addTestSlices(testSlices, classesDir, metadataReaderFactory, springFactories);
+ }
+ }
+ return testSlices;
+ }
+
+ private URL toURL(File file) {
+ try {
+ return file.toURI().toURL();
+ }
+ catch (MalformedURLException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private Properties readSpringFactories(File file) throws IOException {
+ Properties springFactories = new Properties();
+ try (Reader in = new FileReader(file)) {
+ springFactories.load(in);
+ }
+ return springFactories;
+ }
+
+ private void addTestSlices(Properties testSlices, File classesDir, MetadataReaderFactory metadataReaderFactory,
+ Properties springFactories) throws IOException {
+ try (Stream classes = Files.walk(classesDir.toPath())) {
+ classes.filter((path) -> path.toString().endsWith("Test.class"))
+ .map((path) -> getMetadataReader(path, metadataReaderFactory))
+ .filter((metadataReader) -> metadataReader.getClassMetadata().isAnnotation())
+ .forEach((metadataReader) -> addTestSlice(testSlices, springFactories, metadataReader));
+ }
+
+ }
+
+ private MetadataReader getMetadataReader(Path path, MetadataReaderFactory metadataReaderFactory) {
+ try {
+ return metadataReaderFactory.getMetadataReader(new FileSystemResource(path));
+ }
+ catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private void addTestSlice(Properties testSlices, Properties springFactories, MetadataReader metadataReader) {
+ testSlices.setProperty(metadataReader.getClassMetadata().getClassName(),
+ StringUtils.collectionToCommaDelimitedString(
+ getImportedAutoConfiguration(springFactories, metadataReader.getAnnotationMetadata())));
+ }
+
+ private SortedSet getImportedAutoConfiguration(Properties springFactories,
+ AnnotationMetadata annotationMetadata) {
+ Stream importers = findMetaImporters(annotationMetadata);
+ if (annotationMetadata.isAnnotated("org.springframework.boot.autoconfigure.ImportAutoConfiguration")) {
+ importers = Stream.concat(importers, Stream.of(annotationMetadata.getClassName()));
+ }
+ return importers.flatMap(
+ (importer) -> StringUtils.commaDelimitedListToSet(springFactories.getProperty(importer)).stream())
+ .collect(Collectors.toCollection(TreeSet::new));
+ }
+
+ private Stream findMetaImporters(AnnotationMetadata annotationMetadata) {
+ return annotationMetadata.getAnnotationTypes().stream()
+ .filter((annotationType) -> isAutoConfigurationImporter(annotationType, annotationMetadata));
+ }
+
+ private boolean isAutoConfigurationImporter(String annotationType, AnnotationMetadata metadata) {
+ return metadata.getMetaAnnotationTypes(annotationType)
+ .contains("org.springframework.boot.autoconfigure.ImportAutoConfiguration");
+ }
+
+}
diff --git a/buildSrc/src/main/resources/effective-bom-settings.xml b/buildSrc/src/main/resources/effective-bom-settings.xml
new file mode 100644
index 00000000000..d67307453cf
--- /dev/null
+++ b/buildSrc/src/main/resources/effective-bom-settings.xml
@@ -0,0 +1,24 @@
+
+ localRepositoryPath
+
+
+ spring-repositories
+
+ true
+
+
+
+ spring-snapshot
+ https://repo.spring.io/snapshot
+
+ true
+
+
+
+ spring-milestone
+ https://repo.spring.io/milestone
+
+
+
+
+
diff --git a/buildSrc/src/test/java/org/springframework/boot/build/assertj/NodeAssert.java b/buildSrc/src/test/java/org/springframework/boot/build/assertj/NodeAssert.java
new file mode 100644
index 00000000000..452fafaf6fd
--- /dev/null
+++ b/buildSrc/src/test/java/org/springframework/boot/build/assertj/NodeAssert.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.assertj;
+
+import java.io.File;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.assertj.core.api.AbstractAssert;
+import org.assertj.core.api.AssertProvider;
+import org.assertj.core.api.StringAssert;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+/**
+ * AssertJ {@link AssertProvider} for {@link Node} assertions.
+ *
+ * @author Andy Wilkinson
+ */
+public class NodeAssert extends AbstractAssert implements AssertProvider {
+
+ private static final DocumentBuilderFactory FACTORY = DocumentBuilderFactory.newInstance();
+
+ private final XPathFactory xpathFactory = XPathFactory.newInstance();
+
+ private final XPath xpath = this.xpathFactory.newXPath();
+
+ public NodeAssert(File xmlFile) {
+ this(read(xmlFile));
+ }
+
+ public NodeAssert(Node actual) {
+ super(actual, NodeAssert.class);
+ }
+
+ private static Document read(File xmlFile) {
+ try {
+ return FACTORY.newDocumentBuilder().parse(xmlFile);
+ }
+ catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public NodeAssert nodeAtPath(String xpath) {
+ try {
+ return new NodeAssert((Node) this.xpath.evaluate(xpath, this.actual, XPathConstants.NODE));
+ }
+ catch (XPathExpressionException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public StringAssert textAtPath(String xpath) {
+ try {
+ return new StringAssert(
+ (String) this.xpath.evaluate(xpath + "/text()", this.actual, XPathConstants.STRING));
+ }
+ catch (XPathExpressionException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @Override
+ public NodeAssert assertThat() {
+ return this;
+ }
+
+}
diff --git a/buildSrc/src/test/java/org/springframework/boot/build/bom/BomPluginIntegrationTests.java b/buildSrc/src/test/java/org/springframework/boot/build/bom/BomPluginIntegrationTests.java
new file mode 100644
index 00000000000..3bd1c2bb4a8
--- /dev/null
+++ b/buildSrc/src/test/java/org/springframework/boot/build/bom/BomPluginIntegrationTests.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.util.function.Consumer;
+
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import org.springframework.boot.build.DeployedPlugin;
+import org.springframework.boot.build.assertj.NodeAssert;
+import org.springframework.util.FileCopyUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link BomPlugin}.
+ *
+ * @author Andy Wilkinson
+ */
+public class BomPluginIntegrationTests {
+
+ private File projectDir;
+
+ private File buildFile;
+
+ @BeforeEach
+ public void setup(@TempDir File projectDir) throws IOException {
+ this.projectDir = projectDir;
+ this.buildFile = new File(this.projectDir, "build.gradle");
+ }
+
+ @Test
+ void libraryModulesAreIncludedInDependencyManagementOfGeneratedPom() throws IOException {
+ try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) {
+ out.println("plugins {");
+ out.println(" id 'org.springframework.boot.bom'");
+ out.println("}");
+ out.println("bom {");
+ out.println(" library('ActiveMQ', '5.15.10') {");
+ out.println(" group('org.apache.activemq') {");
+ out.println(" modules = [");
+ out.println(" 'activemq-amqp',");
+ out.println(" 'activemq-blueprint'");
+ out.println(" ]");
+ out.println(" }");
+ out.println(" }");
+ out.println("}");
+ }
+ generatePom((pom) -> {
+ assertThat(pom).textAtPath("//properties/activemq.version").isEqualTo("5.15.10");
+ NodeAssert dependency = pom.nodeAtPath("//dependencyManagement/dependencies/dependency[1]");
+ assertThat(dependency).textAtPath("groupId").isEqualTo("org.apache.activemq");
+ assertThat(dependency).textAtPath("artifactId").isEqualTo("activemq-amqp");
+ assertThat(dependency).textAtPath("version").isEqualTo("${activemq.version}");
+ assertThat(dependency).textAtPath("scope").isNullOrEmpty();
+ assertThat(dependency).textAtPath("type").isNullOrEmpty();
+ dependency = pom.nodeAtPath("//dependencyManagement/dependencies/dependency[2]");
+ assertThat(dependency).textAtPath("groupId").isEqualTo("org.apache.activemq");
+ assertThat(dependency).textAtPath("artifactId").isEqualTo("activemq-blueprint");
+ assertThat(dependency).textAtPath("version").isEqualTo("${activemq.version}");
+ assertThat(dependency).textAtPath("scope").isNullOrEmpty();
+ assertThat(dependency).textAtPath("type").isNullOrEmpty();
+ });
+ }
+
+ @Test
+ void libraryPluginsAreIncludedInPluginManagementOfGeneratedPom() throws IOException {
+ try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) {
+ out.println("plugins {");
+ out.println(" id 'org.springframework.boot.bom'");
+ out.println("}");
+ out.println("bom {");
+ out.println(" library('Flyway', '6.0.8') {");
+ out.println(" group('org.flywaydb') {");
+ out.println(" plugins = [");
+ out.println(" 'flyway-maven-plugin'");
+ out.println(" ]");
+ out.println(" }");
+ out.println(" }");
+ out.println("}");
+ }
+ generatePom((pom) -> {
+ assertThat(pom).textAtPath("//properties/flyway.version").isEqualTo("6.0.8");
+ NodeAssert plugin = pom.nodeAtPath("//pluginManagement/plugins/plugin");
+ assertThat(plugin).textAtPath("groupId").isEqualTo("org.flywaydb");
+ assertThat(plugin).textAtPath("artifactId").isEqualTo("flyway-maven-plugin");
+ assertThat(plugin).textAtPath("version").isEqualTo("${flyway.version}");
+ assertThat(plugin).textAtPath("scope").isNullOrEmpty();
+ assertThat(plugin).textAtPath("type").isNullOrEmpty();
+ });
+ }
+
+ @Test
+ void libraryImportsAreIncludedInDependencyManagementOfGeneratedPom() throws Exception {
+ try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) {
+ out.println("plugins {");
+ out.println(" id 'org.springframework.boot.bom'");
+ out.println("}");
+ out.println("bom {");
+ out.println(" library('Jackson Bom', '2.10.0') {");
+ out.println(" group('com.fasterxml.jackson') {");
+ out.println(" imports = [");
+ out.println(" 'jackson-bom'");
+ out.println(" ]");
+ out.println(" }");
+ out.println(" }");
+ out.println("}");
+ }
+ generatePom((pom) -> {
+ assertThat(pom).textAtPath("//properties/jackson-bom.version").isEqualTo("2.10.0");
+ NodeAssert dependency = pom.nodeAtPath("//dependencyManagement/dependencies/dependency");
+ assertThat(dependency).textAtPath("groupId").isEqualTo("com.fasterxml.jackson");
+ assertThat(dependency).textAtPath("artifactId").isEqualTo("jackson-bom");
+ assertThat(dependency).textAtPath("version").isEqualTo("${jackson-bom.version}");
+ assertThat(dependency).textAtPath("scope").isEqualTo("import");
+ assertThat(dependency).textAtPath("type").isEqualTo("pom");
+ });
+ }
+
+ @Test
+ void moduleExclusionsAreIncludedInDependencyManagementOfGeneratedPom() throws IOException {
+ try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) {
+ out.println("plugins {");
+ out.println(" id 'org.springframework.boot.bom'");
+ out.println("}");
+ out.println("bom {");
+ out.println(" library('MySQL', '8.0.18') {");
+ out.println(" group('mysql') {");
+ out.println(" modules = [");
+ out.println(" 'mysql-connector-java' {");
+ out.println(" exclude group: 'com.google.protobuf', module: 'protobuf-java'");
+ out.println(" }");
+ out.println(" ]");
+ out.println(" }");
+ out.println(" }");
+ out.println("}");
+ }
+ generatePom((pom) -> {
+ assertThat(pom).textAtPath("//properties/mysql.version").isEqualTo("8.0.18");
+ NodeAssert dependency = pom.nodeAtPath("//dependencyManagement/dependencies/dependency");
+ assertThat(dependency).textAtPath("groupId").isEqualTo("mysql");
+ assertThat(dependency).textAtPath("artifactId").isEqualTo("mysql-connector-java");
+ assertThat(dependency).textAtPath("version").isEqualTo("${mysql.version}");
+ assertThat(dependency).textAtPath("scope").isNullOrEmpty();
+ assertThat(dependency).textAtPath("type").isNullOrEmpty();
+ NodeAssert exclusion = dependency.nodeAtPath("exclusions/exclusion");
+ assertThat(exclusion).textAtPath("groupId").isEqualTo("com.google.protobuf");
+ assertThat(exclusion).textAtPath("artifactId").isEqualTo("protobuf-java");
+ });
+ }
+
+ private BuildResult runGradle(String... args) {
+ return GradleRunner.create().withDebug(true).withProjectDir(this.projectDir).withArguments(args)
+ .withPluginClasspath().build();
+ }
+
+ private void generatePom(Consumer consumer) {
+ runGradle(DeployedPlugin.GENERATE_POM_TASK_NAME, "-s");
+ File generatedPomXml = new File(this.projectDir, "build/publications/maven/pom-default.xml");
+ try (Reader reader = new FileReader(generatedPomXml)) {
+ System.out.println(FileCopyUtils.copyToString(reader));
+ }
+ catch (IOException ex) {
+
+ }
+ assertThat(generatedPomXml).isFile();
+ consumer.accept(new NodeAssert(generatedPomXml));
+ }
+
+}
diff --git a/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/ArtifactVersionDependencyVersionTests.java b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/ArtifactVersionDependencyVersionTests.java
new file mode 100644
index 00000000000..643baa22199
--- /dev/null
+++ b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/ArtifactVersionDependencyVersionTests.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.version;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link ArtifactVersionDependencyVersion}.
+ *
+ * @author Andy Wilkinson
+ */
+public class ArtifactVersionDependencyVersionTests {
+
+ @Test
+ void parseWhenVersionIsNotAMavenVersionShouldReturnNull() {
+ assertThat(version("1.2.3.1")).isNull();
+ }
+
+ @Test
+ void parseWhenVersionIsAMavenVersionShouldReturnAVersion() {
+ assertThat(version("1.2.3")).isNotNull();
+ }
+
+ @Test
+ void isNewerThanWhenInputIsOlderMajorShouldReturnTrue() {
+ assertThat(version("2.1.2").isNewerThan(version("1.9.0"))).isTrue();
+ }
+
+ @Test
+ void isNewerThanWhenInputIsOlderMinorShouldReturnTrue() {
+ assertThat(version("2.1.2").isNewerThan(version("2.0.2"))).isTrue();
+ }
+
+ @Test
+ void isNewerThanWhenInputIsOlderPatchShouldReturnTrue() {
+ assertThat(version("2.1.2").isNewerThan(version("2.1.1"))).isTrue();
+ }
+
+ @Test
+ void isNewerThanWhenInputIsNewerMajorShouldReturnFalse() {
+ assertThat(version("2.1.2").isNewerThan(version("3.2.1"))).isFalse();
+ }
+
+ @Test
+ void isSameMajorAndNewerThanWhenMinorIsOlderShouldReturnTrue() {
+ assertThat(version("1.10.2").isSameMajorAndNewerThan(version("1.9.0"))).isTrue();
+ }
+
+ @Test
+ void isSameMajorAndNewerThanWhenMajorIsOlderShouldReturnFalse() {
+ assertThat(version("2.0.2").isSameMajorAndNewerThan(version("1.9.0"))).isFalse();
+ }
+
+ @Test
+ void isSameMajorAndNewerThanWhenPatchIsNewerShouldReturnTrue() {
+ assertThat(version("2.1.2").isSameMajorAndNewerThan(version("2.1.1"))).isTrue();
+ }
+
+ @Test
+ void isSameMajorAndNewerThanWhenMinorIsNewerShouldReturnFalse() {
+ assertThat(version("2.1.2").isSameMajorAndNewerThan(version("2.2.1"))).isFalse();
+ }
+
+ @Test
+ void isSameMajorAndNewerThanWhenMajorIsNewerShouldReturnFalse() {
+ assertThat(version("2.1.2").isSameMajorAndNewerThan(version("3.0.1"))).isFalse();
+ }
+
+ @Test
+ void isSameMinorAndNewerThanWhenPatchIsOlderShouldReturnTrue() {
+ assertThat(version("1.10.2").isSameMinorAndNewerThan(version("1.10.1"))).isTrue();
+ }
+
+ @Test
+ void isSameMinorAndNewerThanWhenMinorIsOlderShouldReturnFalse() {
+ assertThat(version("2.1.2").isSameMinorAndNewerThan(version("2.0.1"))).isFalse();
+ }
+
+ @Test
+ void isSameMinorAndNewerThanWhenVersionsAreTheSameShouldReturnFalse() {
+ assertThat(version("2.1.2").isSameMinorAndNewerThan(version("2.1.2"))).isFalse();
+ }
+
+ @Test
+ void isSameMinorAndNewerThanWhenPatchIsNewerShouldReturnFalse() {
+ assertThat(version("2.1.2").isSameMinorAndNewerThan(version("2.1.3"))).isFalse();
+ }
+
+ @Test
+ void isSameMinorAndNewerThanWhenMinorIsNewerShouldReturnFalse() {
+ assertThat(version("2.1.2").isSameMinorAndNewerThan(version("2.0.1"))).isFalse();
+ }
+
+ @Test
+ void isSameMinorAndNewerThanWhenMajorIsNewerShouldReturnFalse() {
+ assertThat(version("3.1.2").isSameMinorAndNewerThan(version("2.0.1"))).isFalse();
+ }
+
+ private ArtifactVersionDependencyVersion version(String version) {
+ return ArtifactVersionDependencyVersion.parse(version);
+ }
+
+}
diff --git a/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/DependencyVersionTests.java b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/DependencyVersionTests.java
new file mode 100644
index 00000000000..2e0b16d7f0f
--- /dev/null
+++ b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/DependencyVersionTests.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.version;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link DependencyVersion}.
+ *
+ * @author Andy Wilkinson
+ */
+public class DependencyVersionTests {
+
+ @Test
+ void parseWhenValidMavenVersionShouldReturnArtifactVersionDependencyVersion() {
+ assertThat(DependencyVersion.parse("1.2.3.Final")).isInstanceOf(ArtifactVersionDependencyVersion.class);
+ }
+
+ @Test
+ void parseWhenReleaseTrainShouldReturnReleaseTrainDependencyVersion() {
+ assertThat(DependencyVersion.parse("Ingalls-SR5")).isInstanceOf(ReleaseTrainDependencyVersion.class);
+ }
+
+ @Test
+ void parseWhenMavenLikeVersionWithNumericQualifieShouldReturnNumericQualifierDependencyVersion() {
+ assertThat(DependencyVersion.parse("1.2.3.4")).isInstanceOf(NumericQualifierDependencyVersion.class);
+ }
+
+ @Test
+ void parseWhenVersionWithLeadingZeroesShouldReturnLeadingZeroesDependencyVersion() {
+ assertThat(DependencyVersion.parse("1.4.01")).isInstanceOf(LeadingZeroesDependencyVersion.class);
+ }
+
+ @Test
+ void parseWhenVersionWithCombinedPatchAndQualifierShouldReturnCombinedPatchAndQualifierDependencyVersion() {
+ assertThat(DependencyVersion.parse("4.0.0M4")).isInstanceOf(CombinedPatchAndQualifierDependencyVersion.class);
+ }
+
+}
diff --git a/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/NumericQualifierDependencyVersionTests.java b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/NumericQualifierDependencyVersionTests.java
new file mode 100644
index 00000000000..5e58d3a99c2
--- /dev/null
+++ b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/NumericQualifierDependencyVersionTests.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.version;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link NumericQualifierDependencyVersion}.
+ *
+ * @author Andy Wilkinson
+ */
+public class NumericQualifierDependencyVersionTests {
+
+ @Test
+ void isNewerThanOnVersionWithNumericQualifierWhenInputHasNoQualifierShouldReturnTrue() {
+ assertThat(version("2.9.9.20190806").isNewerThan(DependencyVersion.parse("2.9.9"))).isTrue();
+ }
+
+ @Test
+ void isNewerThanOnVersionWithNumericQualifierWhenInputHasOlderQualifierShouldReturnTrue() {
+ assertThat(version("2.9.9.20190806").isNewerThan(version("2.9.9.20190805"))).isTrue();
+ }
+
+ @Test
+ void isNewerThanOnVersionWithNumericQualifierWhenInputHasNewerQualifierShouldReturnFalse() {
+ assertThat(version("2.9.9.20190806").isNewerThan(version("2.9.9.20190807"))).isFalse();
+ }
+
+ @Test
+ void isNewerThanOnVersionWithNumericQualifierWhenInputHasSameQualifierShouldReturnFalse() {
+ assertThat(version("2.9.9.20190806").isNewerThan(version("2.9.9.20190806"))).isFalse();
+ }
+
+ private NumericQualifierDependencyVersion version(String version) {
+ return NumericQualifierDependencyVersion.parse(version);
+ }
+
+}
diff --git a/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/ReleaseTrainDependencyVersionTests.java b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/ReleaseTrainDependencyVersionTests.java
new file mode 100644
index 00000000000..6010bfd0750
--- /dev/null
+++ b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/ReleaseTrainDependencyVersionTests.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.bom.bomr.version;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link ReleaseTrainDependencyVersion}.
+ *
+ * @author Andy Wilkinson
+ */
+public class ReleaseTrainDependencyVersionTests {
+
+ @Test
+ void parsingOfANonReleaseTrainVersionReturnsNull() {
+ assertThat(version("5.1.4.RELEASE")).isNull();
+ }
+
+ @Test
+ void parsingOfAReleaseTrainVersionReturnsVersion() {
+ assertThat(version("Lovelace-SR3")).isNotNull();
+ }
+
+ @Test
+ void isNewerThanWhenReleaseTrainIsNewerShouldReturnTrue() {
+ assertThat(version("Lovelace-RELEASE").isNewerThan(version("Kay-SR5"))).isTrue();
+ }
+
+ @Test
+ void isNewerThanWhenVersionIsNewerShouldReturnTrue() {
+ assertThat(version("Kay-SR10").isNewerThan(version("Kay-SR5"))).isTrue();
+ }
+
+ @Test
+ void isNewerThanWhenVersionIsOlderShouldReturnFalse() {
+ assertThat(version("Kay-RELEASE").isNewerThan(version("Kay-SR5"))).isFalse();
+ }
+
+ @Test
+ void isNewerThanWhenReleaseTrainIsOlderShouldReturnFalse() {
+ assertThat(version("Ingalls-RELEASE").isNewerThan(version("Kay-SR5"))).isFalse();
+ }
+
+ @Test
+ void isSameMajorAndNewerWhenWhenReleaseTrainIsNewerShouldReturnTrue() {
+ assertThat(version("Lovelace-RELEASE").isSameMajorAndNewerThan(version("Kay-SR5"))).isTrue();
+ }
+
+ @Test
+ void isSameMajorAndNewerThanWhenReleaseTrainIsOlderShouldReturnFalse() {
+ assertThat(version("Ingalls-RELEASE").isSameMajorAndNewerThan(version("Kay-SR5"))).isFalse();
+ }
+
+ @Test
+ void isSameMajorAndNewerThanWhenVersionIsNewerShouldReturnTrue() {
+ assertThat(version("Kay-SR6").isSameMajorAndNewerThan(version("Kay-SR5"))).isTrue();
+ }
+
+ @Test
+ void isSameMinorAndNewerThanWhenReleaseTrainIsNewerShouldReturnFalse() {
+ assertThat(version("Lovelace-RELEASE").isSameMinorAndNewerThan(version("Kay-SR5"))).isFalse();
+ }
+
+ @Test
+ void isSameMinorAndNewerThanWhenReleaseTrainIsTheSameAndVersionIsNewerShouldReturnTrue() {
+ assertThat(version("Kay-SR6").isSameMinorAndNewerThan(version("Kay-SR5"))).isTrue();
+ }
+
+ @Test
+ void isSameMinorAndNewerThanWhenReleaseTrainAndVersionAreTheSameShouldReturnFalse() {
+ assertThat(version("Kay-SR6").isSameMinorAndNewerThan(version("Kay-SR6"))).isFalse();
+ }
+
+ @Test
+ void isSameMinorAndNewerThanWhenReleaseTrainIsTheSameAndVersionIsOlderShouldReturnFalse() {
+ assertThat(version("Kay-SR6").isSameMinorAndNewerThan(version("Kay-SR7"))).isFalse();
+ }
+
+ private static ReleaseTrainDependencyVersion version(String input) {
+ return ReleaseTrainDependencyVersion.parse(input);
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/test/java/org/springframework/boot/configurationdocs/CompoundConfigurationTableEntryTests.java b/buildSrc/src/test/java/org/springframework/boot/build/context/properties/CompoundConfigurationTableEntryTests.java
similarity index 65%
rename from spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/test/java/org/springframework/boot/configurationdocs/CompoundConfigurationTableEntryTests.java
rename to buildSrc/src/test/java/org/springframework/boot/build/context/properties/CompoundConfigurationTableEntryTests.java
index 0e06b595099..2f229b93b75 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/test/java/org/springframework/boot/configurationdocs/CompoundConfigurationTableEntryTests.java
+++ b/buildSrc/src/test/java/org/springframework/boot/build/context/properties/CompoundConfigurationTableEntryTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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,12 +14,10 @@
* limitations under the License.
*/
-package org.springframework.boot.configurationdocs;
+package org.springframework.boot.build.context.properties;
import org.junit.jupiter.api.Test;
-import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
-
import static org.assertj.core.api.Assertions.assertThat;
/**
@@ -27,21 +25,15 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Brian Clozel
*/
-class CompoundConfigurationTableEntryTests {
+public class CompoundConfigurationTableEntryTests {
private static String NEWLINE = System.lineSeparator();
@Test
void simpleProperty() {
- ConfigurationMetadataProperty firstProp = new ConfigurationMetadataProperty();
- firstProp.setId("spring.test.first");
- firstProp.setType("java.lang.String");
- ConfigurationMetadataProperty secondProp = new ConfigurationMetadataProperty();
- secondProp.setId("spring.test.second");
- secondProp.setType("java.lang.String");
- ConfigurationMetadataProperty thirdProp = new ConfigurationMetadataProperty();
- thirdProp.setId("spring.test.third");
- thirdProp.setType("java.lang.String");
+ ConfigurationProperty firstProp = new ConfigurationProperty("spring.test.first", "java.lang.String");
+ ConfigurationProperty secondProp = new ConfigurationProperty("spring.test.second", "java.lang.String");
+ ConfigurationProperty thirdProp = new ConfigurationProperty("spring.test.third", "java.lang.String");
CompoundConfigurationTableEntry entry = new CompoundConfigurationTableEntry("spring.test",
"This is a description.");
entry.addConfigurationKeys(firstProp, secondProp, thirdProp);
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/test/java/org/springframework/boot/configurationdocs/ConfigurationTableTests.java b/buildSrc/src/test/java/org/springframework/boot/build/context/properties/ConfigurationTableTests.java
similarity index 67%
rename from spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/test/java/org/springframework/boot/configurationdocs/ConfigurationTableTests.java
rename to buildSrc/src/test/java/org/springframework/boot/build/context/properties/ConfigurationTableTests.java
index 2b15a476758..c757c73f16e 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/test/java/org/springframework/boot/configurationdocs/ConfigurationTableTests.java
+++ b/buildSrc/src/test/java/org/springframework/boot/build/context/properties/ConfigurationTableTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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,12 +14,10 @@
* limitations under the License.
*/
-package org.springframework.boot.configurationdocs;
+package org.springframework.boot.build.context.properties;
import org.junit.jupiter.api.Test;
-import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
-
import static org.assertj.core.api.Assertions.assertThat;
/**
@@ -27,23 +25,17 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Brian Clozel
*/
-class ConfigurationTableTests {
+public class ConfigurationTableTests {
private static String NEWLINE = System.lineSeparator();
@Test
void simpleTable() {
ConfigurationTable table = new ConfigurationTable("test");
- ConfigurationMetadataProperty first = new ConfigurationMetadataProperty();
- first.setId("spring.test.prop");
- first.setDefaultValue("something");
- first.setDescription("This is a description.");
- first.setType("java.lang.String");
- ConfigurationMetadataProperty second = new ConfigurationMetadataProperty();
- second.setId("spring.test.other");
- second.setDefaultValue("other value");
- second.setDescription("This is another description.");
- second.setType("java.lang.String");
+ ConfigurationProperty first = new ConfigurationProperty("spring.test.prop", "java.lang.String", "something",
+ "This is a description.", false);
+ ConfigurationProperty second = new ConfigurationProperty("spring.test.other", "java.lang.String", "other value",
+ "This is another description.", false);
table.addEntry(new SingleConfigurationTableEntry(first));
table.addEntry(new SingleConfigurationTableEntry(second));
assertThat(table.toAsciidocTable()).isEqualTo("[cols=\"1,1,2\", options=\"header\"]" + NEWLINE + "|==="
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/test/java/org/springframework/boot/configurationdocs/SingleConfigurationTableEntryTests.java b/buildSrc/src/test/java/org/springframework/boot/build/context/properties/SingleConfigurationTableEntryTests.java
similarity index 63%
rename from spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/test/java/org/springframework/boot/configurationdocs/SingleConfigurationTableEntryTests.java
rename to buildSrc/src/test/java/org/springframework/boot/build/context/properties/SingleConfigurationTableEntryTests.java
index 013320c729b..615cdca6aee 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/test/java/org/springframework/boot/configurationdocs/SingleConfigurationTableEntryTests.java
+++ b/buildSrc/src/test/java/org/springframework/boot/build/context/properties/SingleConfigurationTableEntryTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2020 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,12 +14,10 @@
* limitations under the License.
*/
-package org.springframework.boot.configurationdocs;
+package org.springframework.boot.build.context.properties;
import org.junit.jupiter.api.Test;
-import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
-
import static org.assertj.core.api.Assertions.assertThat;
/**
@@ -27,17 +25,14 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Brian Clozel
*/
-class SingleConfigurationTableEntryTests {
+public class SingleConfigurationTableEntryTests {
private static String NEWLINE = System.lineSeparator();
@Test
void simpleProperty() {
- ConfigurationMetadataProperty property = new ConfigurationMetadataProperty();
- property.setId("spring.test.prop");
- property.setDefaultValue("something");
- property.setDescription("This is a description.");
- property.setType("java.lang.String");
+ ConfigurationProperty property = new ConfigurationProperty("spring.test.prop", "java.lang.String", "something",
+ "This is a description.", false);
SingleConfigurationTableEntry entry = new SingleConfigurationTableEntry(property);
AsciidocBuilder builder = new AsciidocBuilder();
entry.write(builder);
@@ -47,10 +42,8 @@ class SingleConfigurationTableEntryTests {
@Test
void noDefaultValue() {
- ConfigurationMetadataProperty property = new ConfigurationMetadataProperty();
- property.setId("spring.test.prop");
- property.setDescription("This is a description.");
- property.setType("java.lang.String");
+ ConfigurationProperty property = new ConfigurationProperty("spring.test.prop", "java.lang.String", null,
+ "This is a description.", false);
SingleConfigurationTableEntry entry = new SingleConfigurationTableEntry(property);
AsciidocBuilder builder = new AsciidocBuilder();
entry.write(builder);
@@ -60,11 +53,8 @@ class SingleConfigurationTableEntryTests {
@Test
void defaultValueWithPipes() {
- ConfigurationMetadataProperty property = new ConfigurationMetadataProperty();
- property.setId("spring.test.prop");
- property.setDefaultValue("first|second");
- property.setDescription("This is a description.");
- property.setType("java.lang.String");
+ ConfigurationProperty property = new ConfigurationProperty("spring.test.prop", "java.lang.String",
+ "first|second", "This is a description.", false);
SingleConfigurationTableEntry entry = new SingleConfigurationTableEntry(property);
AsciidocBuilder builder = new AsciidocBuilder();
entry.write(builder);
@@ -74,11 +64,8 @@ class SingleConfigurationTableEntryTests {
@Test
void defaultValueWithBackslash() {
- ConfigurationMetadataProperty property = new ConfigurationMetadataProperty();
- property.setId("spring.test.prop");
- property.setDefaultValue("first\\second");
- property.setDescription("This is a description.");
- property.setType("java.lang.String");
+ ConfigurationProperty property = new ConfigurationProperty("spring.test.prop", "java.lang.String",
+ "first\\second", "This is a description.", false);
SingleConfigurationTableEntry entry = new SingleConfigurationTableEntry(property);
AsciidocBuilder builder = new AsciidocBuilder();
entry.write(builder);
@@ -88,10 +75,8 @@ class SingleConfigurationTableEntryTests {
@Test
void descriptionWithPipe() {
- ConfigurationMetadataProperty property = new ConfigurationMetadataProperty();
- property.setId("spring.test.prop");
- property.setDescription("This is a description with a | pipe.");
- property.setType("java.lang.String");
+ ConfigurationProperty property = new ConfigurationProperty("spring.test.prop", "java.lang.String", null,
+ "This is a description with a | pipe.", false);
SingleConfigurationTableEntry entry = new SingleConfigurationTableEntry(property);
AsciidocBuilder builder = new AsciidocBuilder();
entry.write(builder);
@@ -101,10 +86,8 @@ class SingleConfigurationTableEntryTests {
@Test
void mapProperty() {
- ConfigurationMetadataProperty property = new ConfigurationMetadataProperty();
- property.setId("spring.test.prop");
- property.setDescription("This is a description.");
- property.setType("java.util.Map");
+ ConfigurationProperty property = new ConfigurationProperty("spring.test.prop",
+ "java.util.Map", null, "This is a description.", false);
SingleConfigurationTableEntry entry = new SingleConfigurationTableEntry(property);
AsciidocBuilder builder = new AsciidocBuilder();
entry.write(builder);
@@ -115,11 +98,8 @@ class SingleConfigurationTableEntryTests {
@Test
void listProperty() {
String[] defaultValue = new String[] { "first", "second", "third" };
- ConfigurationMetadataProperty property = new ConfigurationMetadataProperty();
- property.setId("spring.test.prop");
- property.setDescription("This is a description.");
- property.setType("java.util.List");
- property.setDefaultValue(defaultValue);
+ ConfigurationProperty property = new ConfigurationProperty("spring.test.prop",
+ "java.util.List", defaultValue, "This is a description.", false);
SingleConfigurationTableEntry entry = new SingleConfigurationTableEntry(property);
AsciidocBuilder builder = new AsciidocBuilder();
entry.write(builder);
diff --git a/buildSrc/src/test/java/org/springframework/boot/build/log4j2/ReproduciblePluginsDatActionTests.java b/buildSrc/src/test/java/org/springframework/boot/build/log4j2/ReproduciblePluginsDatActionTests.java
new file mode 100644
index 00000000000..f482f5353e9
--- /dev/null
+++ b/buildSrc/src/test/java/org/springframework/boot/build/log4j2/ReproduciblePluginsDatActionTests.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 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 org.springframework.boot.build.log4j2;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.apache.logging.log4j.core.config.plugins.processor.PluginCache;
+import org.apache.logging.log4j.core.config.plugins.processor.PluginEntry;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link ReproducibleLog4j2PluginsDatAction}
+ *
+ * @author Andy Wilkinson
+ */
+public class ReproduciblePluginsDatActionTests {
+
+ @Test
+ void postProcessingOrdersCategoriesAndPlugins() throws IOException {
+ Path datFile = Files.createTempFile("Log4j2Plugins", "dat");
+ try {
+ write(datFile);
+ PluginCache cache = new PluginCache();
+ cache.loadCacheFiles(new Vector<>(Arrays.asList(datFile.toUri().toURL())).elements());
+ assertThat(cache.getAllCategories().keySet()).containsExactly("one", "two");
+ assertThat(cache.getCategory("one").keySet()).containsExactly("alpha", "bravo", "charlie");
+ assertThat(cache.getCategory("two").keySet()).containsExactly("delta", "echo", "foxtrot");
+ }
+ finally {
+ Files.delete(datFile);
+ }
+ }
+
+ private void write(Path datFile) throws IOException {
+ PluginCache cache = new PluginCache();
+ createCategory(cache, "two", Arrays.asList("delta", "foxtrot", "echo"));
+ createCategory(cache, "one", Arrays.asList("bravo", "alpha", "charlie"));
+ try (OutputStream output = new FileOutputStream(datFile.toFile())) {
+ cache.writeCache(output);
+ new ReproducibleLog4j2PluginsDatAction().postProcess(datFile.toFile());
+ }
+ }
+
+ private void createCategory(PluginCache cache, String categoryName, List entryNames) {
+ Map category = cache.getCategory(categoryName);
+ for (String entryName : entryNames) {
+ PluginEntry entry = new PluginEntry();
+ entry.setKey(entryName);
+ entry.setClassName("com.example.Plugin");
+ entry.setName("name");
+ entry.setCategory(categoryName);
+ category.put(entryName, entry);
+ }
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/it/executable-war/src/main/java/org/springframework/launcher/it/war/ExampleController.java b/buildSrc/src/test/java/org/springframework/boot/build/mavenplugin/PluginXmlParserTests.java
similarity index 52%
rename from spring-boot-project/spring-boot-tools/spring-boot-loader/src/it/executable-war/src/main/java/org/springframework/launcher/it/war/ExampleController.java
rename to buildSrc/src/test/java/org/springframework/boot/build/mavenplugin/PluginXmlParserTests.java
index 6c0cbe3ca0b..b5a4f838204 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/it/executable-war/src/main/java/org/springframework/launcher/it/war/ExampleController.java
+++ b/buildSrc/src/test/java/org/springframework/boot/build/mavenplugin/PluginXmlParserTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 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,24 +14,27 @@
* limitations under the License.
*/
-package org.springframework.boot.load.it.war;
+package org.springframework.boot.build.mavenplugin;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
+import java.io.File;
+
+import org.junit.jupiter.api.Test;
+
+import org.springframework.boot.build.mavenplugin.PluginXmlParser.Plugin;
/**
- * Simple example Spring MVC Controller.
+ * Tests for {@link PluginXmlParser}.
*
- * @author Phillip Webb
+ * @author Andy Wilkinson
*/
-@Controller
-public class ExampleController {
+public class PluginXmlParserTests {
- @RequestMapping("/")
- @ResponseBody
- public String helloWorld() {
- return "Hello Embedded WAR World!";
+ private final PluginXmlParser parser = new PluginXmlParser();
+
+ @Test
+ void dunno() {
+ Plugin plugin = this.parser.parse(new File("src/test/resources/plugin.xml"));
+ System.out.println(plugin);
}
}
diff --git a/buildSrc/src/test/java/org/springframework/boot/build/optional/OptionalDependenciesPluginIntegrationTests.java b/buildSrc/src/test/java/org/springframework/boot/build/optional/OptionalDependenciesPluginIntegrationTests.java
new file mode 100644
index 00000000000..d5585ee9e6c
--- /dev/null
+++ b/buildSrc/src/test/java/org/springframework/boot/build/optional/OptionalDependenciesPluginIntegrationTests.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2012-2020 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 org.springframework.boot.build.optional;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration tests for {@link OptionalDependenciesPlugin}.
+ *
+ * @author Andy Wilkinson
+ */
+public class OptionalDependenciesPluginIntegrationTests {
+
+ private File projectDir;
+
+ private File buildFile;
+
+ @BeforeEach
+ public void setup(@TempDir File projectDir) throws IOException {
+ this.projectDir = projectDir;
+ this.buildFile = new File(this.projectDir, "build.gradle");
+ }
+
+ @Test
+ void optionalConfigurationIsCreated() throws IOException {
+ try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) {
+ out.println("plugins { id 'org.springframework.boot.optional-dependencies' }");
+ out.println("task printConfigurations {");
+ out.println(" doLast {");
+ out.println(" configurations.all { println it.name }");
+ out.println(" }");
+ out.println("}");
+ }
+ BuildResult buildResult = runGradle("printConfigurations");
+ assertThat(buildResult.getOutput()).contains("optional");
+ }
+
+ @Test
+ void optionalDependenciesAreAddedToMainSourceSetsCompileClasspath() throws IOException {
+ optionalDependenciesAreAddedToSourceSetClasspath("main", "compileClasspath");
+ }
+
+ @Test
+ void optionalDependenciesAreAddedToMainSourceSetsRuntimeClasspath() throws IOException {
+ optionalDependenciesAreAddedToSourceSetClasspath("main", "runtimeClasspath");
+ }
+
+ @Test
+ void optionalDependenciesAreAddedToTestSourceSetsCompileClasspath() throws IOException {
+ optionalDependenciesAreAddedToSourceSetClasspath("test", "compileClasspath");
+ }
+
+ @Test
+ void optionalDependenciesAreAddedToTestSourceSetsRuntimeClasspath() throws IOException {
+ optionalDependenciesAreAddedToSourceSetClasspath("test", "runtimeClasspath");
+ }
+
+ public void optionalDependenciesAreAddedToSourceSetClasspath(String sourceSet, String classpath)
+ throws IOException {
+ try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) {
+ out.println("plugins {");
+ out.println(" id 'org.springframework.boot.optional-dependencies'");
+ out.println(" id 'java'");
+ out.println("}");
+ out.println("repositories {");
+ out.println(" mavenCentral()");
+ out.println("}");
+ out.println("dependencies {");
+ out.println(" optional 'org.springframework:spring-jcl:5.1.2.RELEASE'");
+ out.println("}");
+ out.println("task printClasspath {");
+ out.println(" doLast {");
+ out.println(" println sourceSets." + sourceSet + "." + classpath + ".files");
+ out.println(" }");
+ out.println("}");
+ }
+ BuildResult buildResult = runGradle("printClasspath");
+ assertThat(buildResult.getOutput()).contains("spring-jcl");
+ }
+
+ private BuildResult runGradle(String... args) {
+ return GradleRunner.create().withProjectDir(this.projectDir).withArguments(args).withPluginClasspath().build();
+ }
+
+}
diff --git a/buildSrc/src/test/resources/plugin.xml b/buildSrc/src/test/resources/plugin.xml
new file mode 100644
index 00000000000..f9464b5a5dd
--- /dev/null
+++ b/buildSrc/src/test/resources/plugin.xml
@@ -0,0 +1,911 @@
+
+
+
+
+
+ Spring Boot Maven Plugin
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.2.0.GRADLE-SNAPSHOT
+ spring-boot
+ false
+ true
+
+
+ build-info
+ Generate a {@code build-info.properties} file based the content of the current
+{@link MavenProject}.
+ false
+ true
+ false
+ false
+ false
+ true
+ generate-resources
+ org.springframework.boot.maven.BuildInfoMojo
+ java
+ per-lookup
+ once-per-session
+ 1.4.0
+ true
+
+
+ additionalProperties
+ java.util.Map
+ false
+ true
+ Additional properties to store in the build-info.properties. Each entry is prefixed
+by {@code build.} in the generated build-info.properties.
+
+
+ outputFile
+ java.io.File
+ false
+ true
+ The location of the generated build-info.properties.
+
+
+ project
+ org.apache.maven.project.MavenProject
+ true
+ false
+ The Maven project.
+
+
+ session
+ org.apache.maven.execution.MavenSession
+ true
+ false
+ The Maven session.
+
+
+ time
+ java.lang.String
+ 2.2.0
+ false
+ true
+ The value used for the {@code build.time} property in a form suitable for
+{@link Instant#parse(CharSequence)}. Defaults to {@code session.request.startTime}.
+To disable the {@code build.time} property entirely, use {@code 'off'}.
+
+
+
+
+
+
+
+
+
+ org.sonatype.plexus.build.incremental.BuildContext
+ buildContext
+
+
+
+
+ help
+ Display help information on spring-boot-maven-plugin.<br>
+Call <code>mvn spring-boot:help -Ddetail=true -Dgoal=<goal-name></code> to display parameter details.
+ false
+ false
+ false
+ false
+ false
+ true
+ org.springframework.boot.maven.HelpMojo
+ java
+ per-lookup
+ once-per-session
+ true
+
+
+ detail
+ boolean
+ false
+ true
+ If <code>true</code>, display all settable properties for each goal.
+
+
+ goal
+ java.lang.String
+ false
+ true
+ The name of the goal for which to show help. If unspecified, all goals will be displayed.
+
+
+ indentSize
+ int
+ false
+ true
+ The number of spaces per indentation level, should be positive.
+
+
+ lineLength
+ int
+ false
+ true
+ The maximum length of a display line, should be positive.
+
+
+
+ ${detail}
+ ${goal}
+ ${indentSize}
+ ${lineLength}
+
+
+
+ repackage
+ Repackages existing JAR and WAR archives so that they can be executed from the command
+line using {@literal java -jar}. With <code>layout=NONE</code> can also be used simply
+to package a JAR with nested dependencies (and no main class, so not executable).
+ compile+runtime
+ false
+ true
+ false
+ false
+ false
+ true
+ package
+ org.springframework.boot.maven.RepackageMojo
+ java
+ per-lookup
+ once-per-session
+ 1.0.0
+ compile+runtime
+ true
+
+
+ attach
+ boolean
+ 1.4.0
+ false
+ true
+ Attach the repackaged archive to be installed and deployed.
+
+
+ classifier
+ java.lang.String
+ 1.0.0
+ false
+ true
+ Classifier to add to the repackaged archive. If not given, the main artifact will
+be replaced by the repackaged archive. If given, the classifier will also be used
+to determine the source archive to repackage: if an artifact with that classifier
+already exists, it will be used as source and replaced. If no such artifact exists,
+the main artifact will be used as source and the repackaged archive will be
+attached as a supplemental artifact with that classifier. Attaching the artifact
+allows to deploy it alongside to the original one, see <a href=
+"https://maven.apache.org/plugins/maven-deploy-plugin/examples/deploying-with-classifiers.html"
+>the Maven documentation for more details</a>.
+
+
+ embeddedLaunchScript
+ java.io.File
+ 1.3.0
+ false
+ true
+ The embedded launch script to prepend to the front of the jar if it is fully
+executable. If not specified the 'Spring Boot' default script will be used.
+
+
+ embeddedLaunchScriptProperties
+ java.util.Properties
+ 1.3.0
+ false
+ true
+ Properties that should be expanded in the embedded launch script.
+
+
+ excludeDevtools
+ boolean
+ 1.3.0
+ false
+ true
+ Exclude Spring Boot devtools from the repackaged archive.
+
+
+ excludeGroupIds
+ java.lang.String
+ 1.1.0
+ false
+ true
+ Comma separated list of groupId names to exclude (exact match).
+
+
+ excludes
+ java.util.List
+ 1.1.0
+ false
+ true
+ Collection of artifact definitions to exclude. The {@link Exclude} element defines
+a {@code groupId} and {@code artifactId} mandatory properties and an optional
+{@code classifier} property.
+
+
+ executable
+ boolean
+ 1.3.0
+ false
+ true
+ Make a fully executable jar for *nix machines by prepending a launch script to the
+jar.
+<p>
+Currently, some tools do not accept this format so you may not always be able to
+use this technique. For example, {@code jar -xf} may silently fail to extract a jar
+or war that has been made fully-executable. It is recommended that you only enable
+this option if you intend to execute it directly, rather than running it with
+{@code java -jar} or deploying it to a servlet container.
+
+
+ finalName
+ java.lang.String
+ 1.0.0
+ false
+ false
+ Name of the generated archive.
+
+
+ includeSystemScope
+ boolean
+ 1.4.0
+ false
+ true
+ Include system scoped dependencies.
+
+
+ includes
+ java.util.List
+ 1.2.0
+ false
+ true
+ Collection of artifact definitions to include. The {@link Include} element defines
+a {@code groupId} and {@code artifactId} mandatory properties and an optional
+{@code classifier} property.
+
+
+ layout
+ org.springframework.boot.maven.RepackageMojo$LayoutType
+ 1.0.0
+ false
+ true
+ The type of archive (which corresponds to how the dependencies are laid out inside
+it). Possible values are JAR, WAR, ZIP, DIR, NONE. Defaults to a guess based on the
+archive type.
+
+
+ layoutFactory
+ org.springframework.boot.loader.tools.LayoutFactory
+ 1.5.0
+ false
+ true
+ The layout factory that will be used to create the executable archive if no
+explicit layout is set. Alternative layouts implementations can be provided by 3rd
+parties.
+
+
+ mainClass
+ java.lang.String
+ 1.0.0
+ false
+ true
+ The name of the main class. If not specified the first compiled class found that
+contains a 'main' method will be used.
+
+
+ outputDirectory
+ java.io.File
+ 1.0.0
+ true
+ true
+ Directory containing the generated archive.
+
+
+ project
+ org.apache.maven.project.MavenProject
+ 1.0.0
+ true
+ false
+ The Maven project.
+
+
+ requiresUnpack
+ java.util.List
+ 1.1.0
+ false
+ true
+ A list of the libraries that must be unpacked from fat jars in order to run.
+Specify each library as a {@code <dependency>} with a {@code <groupId>} and a
+{@code <artifactId>} and they will be unpacked at runtime.
+
+
+ skip
+ boolean
+ 1.2.0
+ false
+ true
+ Skip the execution.
+
+
+
+
+ ${spring-boot.repackage.excludeDevtools}
+ ${spring-boot.excludeGroupIds}
+ ${spring-boot.excludes}
+
+
+
+ ${spring-boot.includes}
+ ${spring-boot.repackage.layout}
+
+
+ ${spring-boot.repackage.skip}
+
+
+
+ org.apache.maven.project.MavenProjectHelper
+ projectHelper
+
+
+
+
+ run
+ Run an executable archive application.
+ test
+ false
+ true
+ false
+ false
+ false
+ true
+ validate
+ test-compile
+ org.springframework.boot.maven.RunMojo
+ java
+ per-lookup
+ once-per-session
+ 1.0.0
+ false
+
+
+ addResources
+ boolean
+ 1.0.0
+ false
+ true
+ Add maven resources to the classpath directly, this allows live in-place editing of
+resources. Duplicate resources are removed from {@code target/classes} to prevent
+them to appear twice if {@code ClassLoader.getResources()} is called. Please
+consider adding {@code spring-boot-devtools} to your project instead as it provides
+this feature and many more.
+
+
+ agent
+ java.io.File[]
+ 1.0.0
+ since 2.2.0 in favor of {@code agents}
+ false
+ true
+ Path to agent jar. NOTE: a forked process is required to use this feature.
+
+
+ agents
+ java.io.File[]
+ 2.2.0
+ false
+ true
+ Path to agent jars. NOTE: a forked process is required to use this feature.
+
+
+ arguments
+ java.lang.String[]
+ 1.0.0
+ false
+ true
+ Arguments that should be passed to the application. On command line use commas to
+separate multiple arguments.
+
+
+ classesDirectory
+ java.io.File
+ 1.0.0
+ true
+ true
+ Directory containing the classes and resource files that should be packaged into
+the archive.
+
+
+ environmentVariables
+ java.util.Map
+ 2.1.0
+ false
+ true
+ List of Environment variables that should be associated with the forked process
+used to run the application. NOTE: a forked process is required to use this
+feature.
+
+
+ excludeGroupIds
+ java.lang.String
+ 1.1.0
+ false
+ true
+ Comma separated list of groupId names to exclude (exact match).
+
+
+ excludes
+ java.util.List
+ 1.1.0
+ false
+ true
+ Collection of artifact definitions to exclude. The {@link Exclude} element defines
+a {@code groupId} and {@code artifactId} mandatory properties and an optional
+{@code classifier} property.
+
+
+ folders
+ java.lang.String[]
+ 1.0.0
+ false
+ true
+ Additional folders besides the classes directory that should be added to the
+classpath.
+
+
+ fork
+ boolean
+ 1.2.0
+ false
+ true
+ Flag to indicate if the run processes should be forked. Disabling forking will
+disable some features such as an agent, custom JVM arguments, devtools or
+specifying the working directory to use.
+
+
+ includes
+ java.util.List
+ 1.2.0
+ false
+ true
+ Collection of artifact definitions to include. The {@link Include} element defines
+a {@code groupId} and {@code artifactId} mandatory properties and an optional
+{@code classifier} property.
+
+
+ jvmArguments
+ java.lang.String
+ 1.1.0
+ false
+ true
+ JVM arguments that should be associated with the forked process used to run the
+application. On command line, make sure to wrap multiple values between quotes.
+NOTE: a forked process is required to use this feature.
+
+
+ mainClass
+ java.lang.String
+ 1.0.0
+ false
+ true
+ The name of the main class. If not specified the first compiled class found that
+contains a 'main' method will be used.
+
+
+ noverify
+ boolean
+ 1.0.0
+ false
+ true
+ Flag to say that the agent requires -noverify.
+
+
+ optimizedLaunch
+ boolean
+ 2.2.0
+ false
+ true
+ Whether the JVM's launch should be optimized.
+
+
+ profiles
+ java.lang.String[]
+ 1.3.0
+ false
+ true
+ The spring profiles to activate. Convenience shortcut of specifying the
+'spring.profiles.active' argument. On command line use commas to separate multiple
+profiles.
+
+
+ project
+ org.apache.maven.project.MavenProject
+ 1.0.0
+ true
+ false
+ The Maven project.
+
+
+ skip
+ boolean
+ 1.3.2
+ false
+ true
+ Skip the execution.
+
+
+ systemPropertyVariables
+ java.util.Map
+ 2.1.0
+ false
+ true
+ List of JVM system properties to pass to the process. NOTE: a forked process is
+required to use this feature.
+
+
+ useTestClasspath
+ java.lang.Boolean
+ 1.3.0
+ false
+ true
+ Flag to include the test classpath when running.
+
+
+ workingDirectory
+ java.io.File
+ 1.5.0
+ false
+ true
+ Current working directory to use for the application. If not specified, basedir
+will be used. NOTE: a forked process is required to use this feature.
+
+
+
+ ${spring-boot.run.addResources}
+ ${spring-boot.run.agent}
+ ${spring-boot.run.agents}
+ ${spring-boot.run.arguments}
+
+ ${spring-boot.excludeGroupIds}
+ ${spring-boot.excludes}
+ ${spring-boot.run.folders}
+ ${spring-boot.run.fork}
+ ${spring-boot.includes}
+ ${spring-boot.run.jvmArguments}
+ ${spring-boot.run.main-class}
+ ${spring-boot.run.noverify}
+ ${spring-boot.run.optimizedLaunch}
+ ${spring-boot.run.profiles}
+
+ ${spring-boot.run.skip}
+ ${spring-boot.run.useTestClasspath}
+ ${spring-boot.run.workingDirectory}
+
+
+
+ start
+ Start a spring application. Contrary to the {@code run} goal, this does not block and
+allows other goal to operate on the application. This goal is typically used in
+integration test scenario where the application is started before a test suite and
+stopped after.
+ test
+ false
+ true
+ false
+ false
+ false
+ true
+ pre-integration-test
+ org.springframework.boot.maven.StartMojo
+ java
+ per-lookup
+ once-per-session
+ 1.3.0
+ false
+
+
+ addResources
+ boolean
+ 1.0.0
+ false
+ true
+ Add maven resources to the classpath directly, this allows live in-place editing of
+resources. Duplicate resources are removed from {@code target/classes} to prevent
+them to appear twice if {@code ClassLoader.getResources()} is called. Please
+consider adding {@code spring-boot-devtools} to your project instead as it provides
+this feature and many more.
+
+
+ agent
+ java.io.File[]
+ 1.0.0
+ since 2.2.0 in favor of {@code agents}
+ false
+ true
+ Path to agent jar. NOTE: a forked process is required to use this feature.
+
+
+ agents
+ java.io.File[]
+ 2.2.0
+ false
+ true
+ Path to agent jars. NOTE: a forked process is required to use this feature.
+
+
+ arguments
+ java.lang.String[]
+ 1.0.0
+ false
+ true
+ Arguments that should be passed to the application. On command line use commas to
+separate multiple arguments.
+
+
+ classesDirectory
+ java.io.File
+ 1.0.0
+ true
+ true
+ Directory containing the classes and resource files that should be packaged into
+the archive.
+
+
+ environmentVariables
+ java.util.Map
+ 2.1.0
+ false
+ true
+ List of Environment variables that should be associated with the forked process
+used to run the application. NOTE: a forked process is required to use this
+feature.
+
+
+ excludeGroupIds
+ java.lang.String
+ 1.1.0
+ false
+ true
+ Comma separated list of groupId names to exclude (exact match).
+
+
+ excludes
+ java.util.List
+ 1.1.0
+ false
+ true
+ Collection of artifact definitions to exclude. The {@link Exclude} element defines
+a {@code groupId} and {@code artifactId} mandatory properties and an optional
+{@code classifier} property.
+
+
+ folders
+ java.lang.String[]
+ 1.0.0
+ false
+ true
+ Additional folders besides the classes directory that should be added to the
+classpath.
+
+
+ fork
+ boolean
+ 1.2.0
+ false
+ true
+ Flag to indicate if the run processes should be forked. Disabling forking will
+disable some features such as an agent, custom JVM arguments, devtools or
+specifying the working directory to use.
+
+
+ includes
+ java.util.List
+ 1.2.0
+ false
+ true
+ Collection of artifact definitions to include. The {@link Include} element defines
+a {@code groupId} and {@code artifactId} mandatory properties and an optional
+{@code classifier} property.
+
+
+ jmxName
+ java.lang.String
+ false
+ true
+ The JMX name of the automatically deployed MBean managing the lifecycle of the
+spring application.
+
+
+ jmxPort
+ int
+ false
+ true
+ The port to use to expose the platform MBeanServer if the application is forked.
+
+
+ jvmArguments
+ java.lang.String
+ 1.1.0
+ false
+ true
+ JVM arguments that should be associated with the forked process used to run the
+application. On command line, make sure to wrap multiple values between quotes.
+NOTE: a forked process is required to use this feature.
+
+
+ mainClass
+ java.lang.String
+ 1.0.0
+ false
+ true
+ The name of the main class. If not specified the first compiled class found that
+contains a 'main' method will be used.
+
+
+ maxAttempts
+ int
+ false
+ true
+ The maximum number of attempts to check if the spring application is ready.
+Combined with the "wait" argument, this gives a global timeout value (30 sec by
+default)
+
+
+ noverify
+ boolean
+ 1.0.0
+ false
+ true
+ Flag to say that the agent requires -noverify.
+
+
+ profiles
+ java.lang.String[]
+ 1.3.0
+ false
+ true
+ The spring profiles to activate. Convenience shortcut of specifying the
+'spring.profiles.active' argument. On command line use commas to separate multiple
+profiles.
+
+
+ project
+ org.apache.maven.project.MavenProject
+ 1.0.0
+ true
+ false
+ The Maven project.
+
+
+ skip
+ boolean
+ 1.3.2
+ false
+ true
+ Skip the execution.
+
+
+ systemPropertyVariables
+ java.util.Map
+ 2.1.0
+ false
+ true
+ List of JVM system properties to pass to the process. NOTE: a forked process is
+required to use this feature.
+
+
+ useTestClasspath
+ java.lang.Boolean
+ 1.3.0
+ false
+ true
+ Flag to include the test classpath when running.
+
+
+ wait
+ long
+ false
+ true
+ The number of milli-seconds to wait between each attempt to check if the spring
+application is ready.
+
+
+ workingDirectory
+ java.io.File
+ 1.5.0
+ false
+ true
+ Current working directory to use for the application. If not specified, basedir
+will be used. NOTE: a forked process is required to use this feature.
+
+
+
+ ${spring-boot.run.addResources}
+ ${spring-boot.run.agent}
+ ${spring-boot.run.agents}
+ ${spring-boot.run.arguments}
+
+ ${spring-boot.excludeGroupIds}
+ ${spring-boot.excludes}
+ ${spring-boot.run.folders}
+ ${spring-boot.run.fork}
+ ${spring-boot.includes}
+ ${spring-boot.run.jvmArguments}
+ ${spring-boot.run.main-class}
+ ${spring-boot.run.noverify}
+ ${spring-boot.run.profiles}
+
+ ${spring-boot.run.skip}
+ ${spring-boot.run.useTestClasspath}
+ ${spring-boot.run.workingDirectory}
+
+
+
+ stop
+ Stop a spring application that has been started by the "start" goal. Typically invoked
+once a test suite has completed.
+ false
+ true
+ false
+ false
+ false
+ true
+ post-integration-test
+ org.springframework.boot.maven.StopMojo
+ java
+ per-lookup
+ once-per-session
+ 1.3.0
+ false
+
+
+ fork
+ java.lang.Boolean
+ 1.3.0
+ false
+ true
+ Flag to indicate if process to stop was forked. By default, the value is inherited
+from the {@link MavenProject}. If it is set, it must match the value used to
+{@link StartMojo start} the process.
+
+
+ jmxName
+ java.lang.String
+ false
+ true
+ The JMX name of the automatically deployed MBean managing the lifecycle of the
+application.
+
+
+ jmxPort
+ int
+ false
+ true
+ The port to use to lookup the platform MBeanServer if the application has been
+forked.
+
+
+ project
+ org.apache.maven.project.MavenProject
+ 1.4.1
+ true
+ false
+ The Maven project.
+
+
+ skip
+ boolean
+ 1.3.2
+ false
+ true
+ Skip the execution.
+
+
+
+ ${spring-boot.stop.fork}
+
+ ${spring-boot.stop.skip}
+
+
+
+
+
diff --git a/ci/pipeline.yml b/ci/pipeline.yml
index 73131464cab..e2aeb3871f2 100644
--- a/ci/pipeline.yml
+++ b/ci/pipeline.yml
@@ -3,7 +3,7 @@ resource_types:
type: docker-image
source:
repository: springio/artifactory-resource
- tag: 0.0.10
+ tag: 0.0.11-SNAPSHOT
- name: pull-request
type: docker-image
source:
@@ -209,19 +209,9 @@ jobs:
timeout: ((task-timeout))
image: spring-boot-ci-image
file: git-repo/ci/tasks/build-project.yml
- - in_parallel:
- - task: build-smoke-tests
- timeout: ((task-timeout))
- image: spring-boot-ci-image
- file: git-repo/ci/tasks/build-smoke-tests.yml
- - task: build-integration-tests
- timeout: ((task-timeout))
- image: spring-boot-ci-image
- file: git-repo/ci/tasks/build-integration-tests.yml
- - task: build-deployment-tests
- timeout: ((task-timeout))
- image: spring-boot-ci-image
- file: git-repo/ci/tasks/build-deployment-tests.yml
+ params:
+ GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle-enterprise-cache-username))
+ GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle-enterprise-cache-password))
on_failure:
do:
- put: repo-status-build
@@ -241,16 +231,15 @@ jobs:
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
disable_checksum_uploads: true
- exclude:
- - "**/*.effective-pom"
- - "**/spring-boot-configuration-docs/**"
- - "**/spring-boot-test-support/**"
+ threads: 8
artifact_set:
- include:
- "/**/spring-boot-docs-*.zip"
properties:
"zip.type": "docs"
"zip.deployed": "false"
+ get_params:
+ threads: 8
on_failure:
do:
- put: slack-alert
@@ -283,19 +272,6 @@ jobs:
timeout: ((task-timeout))
image: spring-boot-ci-image
file: git-repo/ci/tasks/build-pr-project.yml
- - in_parallel:
- - task: build-smoke-tests
- timeout: ((task-timeout))
- image: spring-boot-ci-image
- file: git-repo/ci/tasks/build-smoke-tests.yml
- - task: build-integration-tests
- timeout: ((task-timeout))
- image: spring-boot-ci-image
- file: git-repo/ci/tasks/build-integration-tests.yml
- - task: build-deployment-tests
- timeout: ((task-timeout))
- image: spring-boot-ci-image
- file: git-repo/ci/tasks/build-deployment-tests.yml
on_success:
put: git-pull-request
params:
@@ -321,19 +297,9 @@ jobs:
timeout: ((task-timeout))
image: spring-boot-jdk11-ci-image
file: git-repo/ci/tasks/build-project.yml
- - in_parallel:
- - task: build-smoke-tests
- timeout: ((task-timeout))
- image: spring-boot-jdk11-ci-image
- file: git-repo/ci/tasks/build-smoke-tests.yml
- - task: build-integration-tests
- timeout: ((task-timeout))
- image: spring-boot-jdk11-ci-image
- file: git-repo/ci/tasks/build-integration-tests.yml
- - task: build-deployment-tests
- timeout: ((task-timeout))
- image: spring-boot-jdk11-ci-image
- file: git-repo/ci/tasks/build-deployment-tests.yml
+ params:
+ GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle-enterprise-cache-username))
+ GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle-enterprise-cache-password))
on_failure:
do:
- put: repo-status-jdk11-build
@@ -362,24 +328,14 @@ jobs:
- put: repo-status-jdk13-build
params: { state: "pending", commit: "git-repo" }
- do:
- - task: build-project
- privileged: true
- timeout: ((task-timeout))
- image: spring-boot-jdk13-ci-image
- file: git-repo/ci/tasks/build-project.yml
- - in_parallel:
- - task: build-smoke-tests
- timeout: ((task-timeout))
- image: spring-boot-jdk13-ci-image
- file: git-repo/ci/tasks/build-smoke-tests.yml
- - task: build-integration-tests
- timeout: ((task-timeout))
- image: spring-boot-jdk13-ci-image
- file: git-repo/ci/tasks/build-integration-tests.yml
- - task: build-deployment-tests
- timeout: ((task-timeout))
- image: spring-boot-jdk13-ci-image
- file: git-repo/ci/tasks/build-deployment-tests.yml
+ - task: build-project
+ privileged: true
+ timeout: ((task-timeout))
+ image: spring-boot-jdk13-ci-image
+ file: git-repo/ci/tasks/build-project.yml
+ params:
+ GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle-enterprise-cache-username))
+ GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle-enterprise-cache-password))
on_failure:
do:
- put: repo-status-jdk13-build
@@ -412,6 +368,9 @@ jobs:
tags:
- WIN64
timeout: ((task-timeout))
+ params:
+ GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle-enterprise-cache-username))
+ GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle-enterprise-cache-password))
on_failure:
do:
- put: slack-alert
@@ -437,6 +396,8 @@ jobs:
file: git-repo/ci/tasks/stage.yml
params:
RELEASE_TYPE: M
+ GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle-enterprise-cache-username))
+ GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle-enterprise-cache-password))
- put: artifactory-repo
params:
<<: *artifactory-params
@@ -455,6 +416,8 @@ jobs:
file: git-repo/ci/tasks/stage.yml
params:
RELEASE_TYPE: RC
+ GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle-enterprise-cache-username))
+ GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle-enterprise-cache-password))
- put: artifactory-repo
params:
<<: *artifactory-params
@@ -473,6 +436,8 @@ jobs:
file: git-repo/ci/tasks/stage.yml
params:
RELEASE_TYPE: RELEASE
+ GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle-enterprise-cache-username))
+ GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle-enterprise-cache-password))
- put: artifactory-repo
params:
<<: *artifactory-params
diff --git a/ci/scripts/build-deployment-tests.sh b/ci/scripts/build-deployment-tests.sh
deleted file mode 100755
index c93fd090083..00000000000
--- a/ci/scripts/build-deployment-tests.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-set -e
-
-source $(dirname $0)/common.sh
-repository=$(pwd)/distribution-repository
-
-pushd git-repo > /dev/null
-run_maven -f spring-boot-tests/spring-boot-deployment-tests/pom.xml clean install -U -Dfull -Drepository=file://${repository}
-popd > /dev/null
diff --git a/ci/scripts/build-integration-tests.sh b/ci/scripts/build-integration-tests.sh
deleted file mode 100755
index 953eaebb1c7..00000000000
--- a/ci/scripts/build-integration-tests.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-set -e
-
-source $(dirname $0)/common.sh
-repository=$(pwd)/distribution-repository
-
-pushd git-repo > /dev/null
-run_maven -f spring-boot-tests/spring-boot-integration-tests/pom.xml clean install -U -Dfull -Drepository=file://${repository}
-popd > /dev/null
diff --git a/ci/scripts/build-project-windows.bat b/ci/scripts/build-project-windows.bat
index efade5d1acf..aa8b62e1abd 100755
--- a/ci/scripts/build-project-windows.bat
+++ b/ci/scripts/build-project-windows.bat
@@ -1,6 +1,4 @@
SET "JAVA_HOME=C:\opt\jdk-8"
SET PATH=%PATH%;C:\Program Files\Git\usr\bin
cd git-repo
-
-echo ".\mvnw clean install" > build.log
-.\mvnw clean install -U >> build.log 2>&1 || (sleep 1 && tail -n 3000 build.log && exit 1)
\ No newline at end of file
+.\gradlew --no-daemon --max-workers=4 build
diff --git a/ci/scripts/build-project.sh b/ci/scripts/build-project.sh
index da3c44e4723..767884cbc9b 100755
--- a/ci/scripts/build-project.sh
+++ b/ci/scripts/build-project.sh
@@ -5,6 +5,5 @@ source $(dirname $0)/common.sh
repository=$(pwd)/distribution-repository
pushd git-repo > /dev/null
-run_maven -N clean verify
-run_maven -f spring-boot-project/pom.xml clean deploy -U -Dfull -DaltDeploymentRepository=distribution::default::file://${repository}
+./gradlew --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
popd > /dev/null
diff --git a/ci/scripts/build-smoke-tests.sh b/ci/scripts/build-smoke-tests.sh
deleted file mode 100755
index cfb839c0efa..00000000000
--- a/ci/scripts/build-smoke-tests.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-set -e
-
-source $(dirname $0)/common.sh
-repository=$(pwd)/distribution-repository
-
-pushd git-repo > /dev/null
-run_maven -f spring-boot-tests/spring-boot-smoke-tests/pom.xml clean install -U -Dfull -Drepository=file://${repository}
-popd > /dev/null
diff --git a/ci/scripts/stage.sh b/ci/scripts/stage.sh
index 2ff3a71bb37..79aa2d58461 100755
--- a/ci/scripts/stage.sh
+++ b/ci/scripts/stage.sh
@@ -12,7 +12,7 @@ git clone git-repo stage-git-repo > /dev/null
pushd stage-git-repo > /dev/null
-snapshotVersion=$( get_revision_from_pom )
+snapshotVersion=$( awk -F '=' '$1 == "version" { print $2 }' gradle.properties )
if [[ $RELEASE_TYPE = "M" ]]; then
stageVersion=$( get_next_milestone_release $snapshotVersion)
nextVersion=$snapshotVersion
@@ -27,23 +27,20 @@ else
fi
echo "Staging $stageVersion (next version will be $nextVersion)"
+sed -i "s/version=$snapshotVersion/version=$stageVersion/" gradle.properties
-set_revision_to_pom "$stageVersion"
git config user.name "Spring Buildmaster" > /dev/null
git config user.email "buildmaster@springframework.org" > /dev/null
-git add pom.xml > /dev/null
+git add gradle.properties > /dev/null
git commit -m"Release v$stageVersion" > /dev/null
git tag -a "v$stageVersion" -m"Release v$stageVersion" > /dev/null
-run_maven -f spring-boot-project/pom.xml clean deploy -U -Dfull -DaltDeploymentRepository=distribution::default::file://${repository}
-run_maven -f spring-boot-tests/spring-boot-smoke-tests/pom.xml clean install -U -Dfull -Drepository=file://${repository}
-run_maven -f spring-boot-tests/spring-boot-integration-tests/pom.xml clean install -U -Dfull -Drepository=file://${repository}
-run_maven -f spring-boot-tests/spring-boot-deployment-tests/pom.xml clean install -U -Dfull -Drepository=file://${repository}
+./gradlew --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
git reset --hard HEAD^ > /dev/null
if [[ $nextVersion != $snapshotVersion ]]; then
echo "Setting next development version (v$nextVersion)"
- set_revision_to_pom "$nextVersion"
+ sed -i "s/version=$snapshotVersion/version=$nextVersion/" gradle.properties
git add pom.xml > /dev/null
git commit -m"Next development version (v$nextVersion)" > /dev/null
fi;
diff --git a/ci/tasks/build-deployment-tests.yml b/ci/tasks/build-deployment-tests.yml
deleted file mode 100644
index e45a4c2a301..00000000000
--- a/ci/tasks/build-deployment-tests.yml
+++ /dev/null
@@ -1,10 +0,0 @@
----
-platform: linux
-inputs:
-- name: git-repo
-- name: distribution-repository
-caches:
-- path: maven
-- path: gradle
-run:
- path: git-repo/ci/scripts/build-deployment-tests.sh
diff --git a/ci/tasks/build-integration-tests.yml b/ci/tasks/build-integration-tests.yml
deleted file mode 100644
index bc96aa6cef4..00000000000
--- a/ci/tasks/build-integration-tests.yml
+++ /dev/null
@@ -1,10 +0,0 @@
----
-platform: linux
-inputs:
-- name: git-repo
-- name: distribution-repository
-caches:
-- path: maven
-- path: gradle
-run:
- path: git-repo/ci/scripts/build-integration-tests.sh
diff --git a/ci/tasks/build-pr-project.yml b/ci/tasks/build-pr-project.yml
index b4576a4849f..c3dbfc8c05a 100644
--- a/ci/tasks/build-pr-project.yml
+++ b/ci/tasks/build-pr-project.yml
@@ -7,5 +7,8 @@ outputs:
caches:
- path: maven
- path: gradle
+params:
+ CI: true
+ GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: git-repo/ci/scripts/build-project.sh
diff --git a/ci/tasks/build-project.yml b/ci/tasks/build-project.yml
index 50e222c7d24..a22b6f5d9de 100644
--- a/ci/tasks/build-project.yml
+++ b/ci/tasks/build-project.yml
@@ -8,6 +8,11 @@ caches:
- path: maven
- path: gradle
- path: embedmongo
+params:
+ CI: true
+ GRADLE_ENTERPRISE_CACHE_USERNAME:
+ GRADLE_ENTERPRISE_CACHE_PASSWORD:
+ GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
@@ -16,4 +21,3 @@ run:
source /docker-lib.sh
start_docker
${PWD}/git-repo/ci/scripts/build-project.sh
-
diff --git a/ci/tasks/build-smoke-tests.yml b/ci/tasks/build-smoke-tests.yml
deleted file mode 100644
index a38dfb2ef9f..00000000000
--- a/ci/tasks/build-smoke-tests.yml
+++ /dev/null
@@ -1,10 +0,0 @@
----
-platform: linux
-inputs:
-- name: git-repo
-- name: distribution-repository
-caches:
-- path: maven
-- path: gradle
-run:
- path: git-repo/ci/scripts/build-smoke-tests.sh
diff --git a/ci/tasks/stage.yml b/ci/tasks/stage.yml
index f486313ae91..dd21db74f8e 100644
--- a/ci/tasks/stage.yml
+++ b/ci/tasks/stage.yml
@@ -7,6 +7,10 @@ outputs:
- name: distribution-repository
params:
RELEASE_TYPE:
+ CI: true
+ GRADLE_ENTERPRISE_CACHE_USERNAME:
+ GRADLE_ENTERPRISE_CACHE_PASSWORD:
+ GRADLE_ENTERPRISE_URL: https://ge.spring.io
caches:
- path: maven
- path: gradle
diff --git a/eclipse/spring-boot-project.setup b/eclipse/spring-boot-project.setup
index 79100f5284d..2048d3b1bd0 100644
--- a/eclipse/spring-boot-project.setup
+++ b/eclipse/spring-boot-project.setup
@@ -4,13 +4,12 @@
xmlns:xmi="http://www.omg.org/XMI"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdt="http://www.eclipse.org/oomph/setup/jdt/1.0"
- xmlns:maven="http://www.eclipse.org/oomph/setup/maven/1.0"
xmlns:predicates="http://www.eclipse.org/oomph/predicates/1.0"
xmlns:setup="http://www.eclipse.org/oomph/setup/1.0"
xmlns:setup.p2="http://www.eclipse.org/oomph/setup/p2/1.0"
xmlns:setup.workingsets="http://www.eclipse.org/oomph/setup/workingsets/1.0"
xmlns:workingsets="http://www.eclipse.org/oomph/workingsets/1.0"
- xsi:schemaLocation="http://www.eclipse.org/oomph/setup/jdt/1.0 https://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/JDT.ecore http://www.eclipse.org/oomph/setup/maven/1.0 https://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/Maven.ecore http://www.eclipse.org/oomph/predicates/1.0 https://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/Predicates.ecore http://www.eclipse.org/oomph/setup/workingsets/1.0 https://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/SetupWorkingSets.ecore http://www.eclipse.org/oomph/workingsets/1.0 https://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/WorkingSets.ecore"
+ xsi:schemaLocation="http://www.eclipse.org/oomph/setup/jdt/1.0 https://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/JDT.ecore http://www.eclipse.org/oomph/predicates/1.0 https://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/Predicates.ecore http://www.eclipse.org/oomph/setup/workingsets/1.0 https://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/SetupWorkingSets.ecore http://www.eclipse.org/oomph/workingsets/1.0 https://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/WorkingSets.ecore"
name="spring.boot.2.3.x"
label="Spring Boot 2.3.x">
-
-
-
- spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin
-
-
-
search) {
+ def query = search.collect { name, value ->
+ "search.names=${encodeURL(name)}&search.values=${encodeURL(value)}"
+ }.join('&')
+
+ "$gradleEnterprise.buildScan.server/scans?$query"
+}
+
+String encodeURL(String url) {
+ URLEncoder.encode(url, 'UTF-8')
+}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000000..cc4fdc293d0
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
similarity index 91%
rename from spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/gradle/wrapper/gradle-wrapper.properties
rename to gradle/wrapper/gradle-wrapper.properties
index 290541c7386..94920145f34 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/gradlew b/gradlew
similarity index 75%
rename from spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/gradlew
rename to gradlew
index cccdd3d517f..2fe81a7d95e 100755
--- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/gradlew
+++ b/gradlew
@@ -1,5 +1,21 @@
#!/usr/bin/env sh
+#
+# Copyright 2015 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.
+#
+
##############################################################################
##
## Gradle start up script for UN*X
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@@ -109,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
@@ -138,19 +154,19 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
- i=$((i+1))
+ i=`expr $i + 1`
done
case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
@@ -159,14 +175,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
-APP_ARGS=$(save "$@")
+APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
-fi
-
exec "$JAVACMD" "$@"
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/gradlew.bat b/gradlew.bat
similarity index 75%
rename from spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/gradlew.bat
rename to gradlew.bat
index f9553162f12..9618d8d9607 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/gradlew.bat
+++ b/gradlew.bat
@@ -1,3 +1,19 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index dd169ece2b2..00000000000
--- a/pom.xml
+++ /dev/null
@@ -1,384 +0,0 @@
-
-
- 4.0.0
- org.springframework.boot
- spring-boot-build
- ${revision}
- pom
- Spring Boot Build
- Spring Boot Build
-
- 2.3.0.BUILD-SNAPSHOT
- ${basedir}
-
-
-
-
-
- default
-
-
- !disable-spring-boot-default-profile
-
-
-
- 0.0.17
- 0.0.4.RELEASE
-
-
-
-
- org.apache.maven.plugins
- maven-checkstyle-plugin
- 3.0.0
-
-
- com.puppycrawl.tools
- checkstyle
- 8.22
-
-
- io.spring.javaformat
- spring-javaformat-checkstyle
- ${spring-javaformat.version}
-
-
- io.spring.nohttp
- nohttp-checkstyle
- ${nohttp-checkstyle.version}
-
-
-
-
- checkstyle-validation
- validate
-
- ${disable.checks}
- src/checkstyle/checkstyle.xml
- src/checkstyle/checkstyle-suppressions.xml
- true
- main.basedir=${main.basedir}
- UTF-8
-
-
- check
-
-
-
- nohttp-checkstyle-validation
- validate
-
- ${disable.checks}
- src/checkstyle/nohttp-checkstyle.xml
- src/checkstyle/nohttp-checkstyle-suppressions.xml
- main.basedir=${main.basedir}
- UTF-8
- ${basedir}
- **/*
- **/.git/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class,**/spring-boot-gradle-plugin/build/**,**/spring-boot-gradle-plugin/bin/**
-
-
- check
-
- false
-
-
-
-
- io.spring.javaformat
- spring-javaformat-maven-plugin
- ${spring-javaformat.version}
-
-
- validate
-
- ${disable.checks}
-
-
- validate
-
-
-
-
-
-
-
- spring-boot-project
-
- spring-boot-tests
-
-
-
-
- m2e
-
-
- m2e.version
-
-
-
- spring-boot-project
- spring-boot-tests
-
-
-
- repository
-
-
- repository
-
-
-
-
- repository
- ${repository}
-
- true
-
-
-
-
-
- repository
- ${repository}
-
- true
-
-
-
-
-
-
-
-
- central
- https://repo.maven.apache.org/maven2
-
- false
-
-
-
- spring-milestone
- Spring Milestone
- https://repo.spring.io/milestone
-
- false
-
-
-
- spring-snapshot
- Spring Snapshot
- https://repo.spring.io/snapshot
-
- true
-
-
-
- rabbit-milestone
- Rabbit Milestone
- https://dl.bintray.com/rabbitmq/maven-milestones
-
- false
-
-
-
-
-
- central
- https://repo.maven.apache.org/maven2
-
- false
-
-
-
- spring-release
- Spring Release
- https://repo.spring.io/release
-
-
- spring-milestone
- Spring Milestone
- https://repo.spring.io/milestone
-
- false
-
-
-
- spring-snapshot
- Spring Snapshot
- https://repo.spring.io/snapshot
-
- true
-
-
-
-
-
-
-
-
- org.eclipse.m2e
- lifecycle-mapping
- 1.0.0
-
-
-
-
-
- org.apache.maven.plugins
- maven-checkstyle-plugin
- [1,)
-
- check
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
- [1,)
-
- copy
- copy-dependencies
- unpack
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-enforcer-plugin
- [1,)
-
- enforce
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-invoker-plugin
- [1,)
-
- install
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-plugin-plugin
- [1,)
-
- descriptor
- helpmojo
-
-
-
-
-
-
-
-
- org.basepom.maven
- duplicate-finder-maven-plugin
- [1,)
-
- check
-
-
-
-
-
-
-
-
- org.codehaus.mojo
- build-helper-maven-plugin
- [1,)
-
- reserve-network-port
-
-
-
-
-
-
-
-
- org.codehaus.mojo
- flatten-maven-plugin
- [1,)
-
- flatten
-
-
-
-
-
-
-
-
- org.jetbrains.kotlin
- kotlin-maven-plugin
- [1,)
-
- compile
- test-compile
-
-
-
-
-
-
-
-
- org.jooq
- jooq-codegen-maven
- [1,)
-
- generate
-
-
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
- [1,)
-
- build-info
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 00000000000..43d7fd1cfba
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,71 @@
+pluginManagement {
+ repositories {
+ mavenCentral()
+ gradlePluginPortal()
+ maven { url 'https://repo.spring.io/snapshot' }
+ }
+ resolutionStrategy {
+ eachPlugin {
+ if (requested.id.id == 'org.jetbrains.kotlin.jvm') {
+ useVersion "${kotlinVersion}"
+ }
+ if (requested.id.id == 'org.jetbrains.kotlin.plugin.spring') {
+ useVersion "${kotlinVersion}"
+ }
+ }
+ }
+}
+
+plugins {
+ id 'com.gradle.enterprise' version '3.1'
+}
+
+apply from: "$rootDir/gradle/build-scan-user-data.gradle"
+gradleEnterprise {
+ buildScan {
+ captureTaskInputFiles = true
+ obfuscation {
+ ipAddresses { addresses -> addresses.collect { address -> '0.0.0.0'} }
+ }
+ publishAlwaysIf(System.getenv('GRADLE_ENTERPRISE_URL') != null)
+ server = 'https://ge.spring.io'
+ }
+}
+
+apply from: new File(settingsDir, 'gradle/build-cache-settings.gradle')
+
+rootProject.name='spring-boot-build'
+
+include 'spring-boot-project:spring-boot-dependencies'
+include 'spring-boot-project:spring-boot-parent'
+include 'spring-boot-project:spring-boot-tools:spring-boot-antlib'
+include 'spring-boot-project:spring-boot-tools:spring-boot-autoconfigure-processor'
+include 'spring-boot-project:spring-boot-tools:spring-boot-configuration-metadata'
+include 'spring-boot-project:spring-boot-tools:spring-boot-configuration-processor'
+include 'spring-boot-project:spring-boot-tools:spring-boot-gradle-plugin'
+include 'spring-boot-project:spring-boot-tools:spring-boot-loader'
+include 'spring-boot-project:spring-boot-tools:spring-boot-loader-tools'
+include 'spring-boot-project:spring-boot-tools:spring-boot-maven-plugin'
+include 'spring-boot-project:spring-boot-tools:spring-boot-test-support'
+include 'spring-boot-project:spring-boot'
+include 'spring-boot-project:spring-boot-autoconfigure'
+include 'spring-boot-project:spring-boot-actuator'
+include 'spring-boot-project:spring-boot-actuator-autoconfigure'
+include 'spring-boot-project:spring-boot-cli'
+include 'spring-boot-project:spring-boot-devtools'
+include 'spring-boot-project:spring-boot-docs'
+include 'spring-boot-project:spring-boot-properties-migrator'
+include 'spring-boot-project:spring-boot-test'
+include 'spring-boot-project:spring-boot-test-autoconfigure'
+include 'spring-boot-tests:spring-boot-deployment-tests'
+include 'spring-boot-tests:spring-boot-integration-tests:spring-boot-configuration-processor-tests'
+include 'spring-boot-tests:spring-boot-integration-tests:spring-boot-launch-script-tests'
+include 'spring-boot-tests:spring-boot-integration-tests:spring-boot-server-tests'
+
+new File("$rootDir/spring-boot-project/spring-boot-starters").eachFileMatch(groovy.io.FileType.DIRECTORIES, ~/spring-boot-starter.*/) {
+ include "spring-boot-project:spring-boot-starters:$it.name"
+}
+
+new File("$rootDir/spring-boot-tests/spring-boot-smoke-tests").eachFileMatch(groovy.io.FileType.DIRECTORIES, ~/spring-boot-smoke-test.*/) {
+ include "spring-boot-tests:spring-boot-smoke-tests:$it.name"
+}
diff --git a/spring-boot-project/pom.xml b/spring-boot-project/pom.xml
deleted file mode 100644
index 2e893ff2819..00000000000
--- a/spring-boot-project/pom.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
- 4.0.0
-
- org.springframework.boot
- spring-boot-build
- ${revision}
-
- spring-boot-project
- pom
- Spring Boot Project
- Spring Boot Project
-
- ${basedir}/..
-
-
- spring-boot-dependencies
- spring-boot-parent
- spring-boot
- spring-boot-actuator
- spring-boot-actuator-autoconfigure
- spring-boot-autoconfigure
- spring-boot-devtools
- spring-boot-properties-migrator
- spring-boot-test
- spring-boot-test-autoconfigure
- spring-boot-tools
- spring-boot-starters
- spring-boot-cli
- spring-boot-docs
-
-
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle
new file mode 100644
index 00000000000..5c7076e9ef4
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle
@@ -0,0 +1,185 @@
+plugins {
+ id 'java-library'
+ id 'org.asciidoctor.jvm.convert'
+ id 'org.asciidoctor.jvm.pdf'
+ id 'org.springframework.boot.auto-configuration'
+ id 'org.springframework.boot.conventions'
+ id 'org.springframework.boot.deployed'
+ id 'org.springframework.boot.optional-dependencies'
+}
+
+description = 'Spring Boot Actuator AutoConfigure'
+
+configurations {
+ asciidoctorExtensions
+ documentation
+}
+
+dependencies {
+ asciidoctorExtensions enforcedPlatform(project(':spring-boot-project:spring-boot-dependencies'))
+ asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor'
+
+ api project(':spring-boot-project:spring-boot-actuator')
+
+ implementation enforcedPlatform(project(':spring-boot-project:spring-boot-dependencies'))
+ implementation project(':spring-boot-project:spring-boot')
+ implementation project(':spring-boot-project:spring-boot-autoconfigure')
+ implementation 'com.fasterxml.jackson.core:jackson-databind'
+ implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
+ implementation 'org.springframework:spring-core'
+ implementation 'org.springframework:spring-context'
+
+ optional enforcedPlatform(project(':spring-boot-project:spring-boot-dependencies'))
+ optional 'ch.qos.logback:logback-classic'
+ optional 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml'
+ optional 'com.github.ben-manes.caffeine:caffeine'
+ optional 'com.hazelcast:hazelcast'
+ optional 'com.hazelcast:hazelcast-spring'
+ optional 'com.sun.mail:jakarta.mail'
+ optional 'com.zaxxer:HikariCP'
+ optional 'io.dropwizard.metrics:metrics-jmx'
+ optional 'io.lettuce:lettuce-core'
+ optional 'io.micrometer:micrometer-core'
+ optional 'io.micrometer:micrometer-jersey2'
+ optional 'io.micrometer:micrometer-registry-appoptics'
+ optional 'io.micrometer:micrometer-registry-atlas'
+ optional 'io.micrometer:micrometer-registry-datadog'
+ optional 'io.micrometer:micrometer-registry-dynatrace'
+ optional 'io.micrometer:micrometer-registry-elastic'
+ optional 'io.micrometer:micrometer-registry-ganglia'
+ optional 'io.micrometer:micrometer-registry-graphite'
+ optional 'io.micrometer:micrometer-registry-humio'
+ optional 'io.micrometer:micrometer-registry-influx'
+ optional 'io.micrometer:micrometer-registry-jmx'
+ optional 'io.micrometer:micrometer-registry-kairos'
+ optional 'io.micrometer:micrometer-registry-new-relic'
+ optional 'io.micrometer:micrometer-registry-prometheus'
+ optional 'io.micrometer:micrometer-registry-stackdriver'
+ optional 'io.prometheus:simpleclient_pushgateway'
+ optional 'io.micrometer:micrometer-registry-signalfx'
+ optional 'io.micrometer:micrometer-registry-statsd'
+ optional 'io.micrometer:micrometer-registry-wavefront'
+ optional 'io.projectreactor.netty:reactor-netty'
+ optional 'io.searchbox:jest'
+ optional 'jakarta.jms:jakarta.jms-api'
+ optional 'jakarta.servlet:jakarta.servlet-api'
+ optional 'javax.cache:cache-api'
+ optional 'net.sf.ehcache:ehcache'
+ optional 'org.apache.activemq:activemq-broker'
+ optional 'org.apache.commons:commons-dbcp2'
+ optional 'org.apache.kafka:kafka-clients'
+ optional 'org.apache.tomcat.embed:tomcat-embed-core'
+ optional 'org.apache.tomcat.embed:tomcat-embed-el'
+ optional 'org.apache.tomcat:tomcat-jdbc'
+ optional 'org.aspectj:aspectjweaver'
+ optional 'org.eclipse.jetty:jetty-server'
+ optional 'org.elasticsearch:elasticsearch'
+ optional 'org.elasticsearch.client:elasticsearch-rest-client'
+ optional 'org.flywaydb:flyway-core'
+ optional 'org.glassfish.jersey.core:jersey-server'
+ optional 'org.glassfish.jersey.containers:jersey-container-servlet-core'
+ optional 'org.hibernate:hibernate-core'
+ optional 'org.hibernate.validator:hibernate-validator'
+ optional 'org.influxdb:influxdb-java'
+ optional 'org.jolokia:jolokia-core'
+ optional 'org.infinispan:infinispan-spring4-embedded'
+ optional 'org.liquibase:liquibase-core'
+ optional 'org.mongodb:mongodb-driver-async'
+ optional 'org.mongodb:mongodb-driver-reactivestreams'
+ optional 'org.springframework:spring-jdbc'
+ optional 'org.springframework:spring-jms'
+ optional 'org.springframework:spring-messaging'
+ optional 'org.springframework:spring-webflux'
+ optional 'org.springframework:spring-webmvc'
+ optional 'org.springframework.amqp:spring-rabbit'
+ optional 'org.springframework.data:spring-data-cassandra'
+ optional 'org.springframework.data:spring-data-couchbase'
+ optional 'org.springframework.data:spring-data-ldap'
+ optional 'org.springframework.data:spring-data-mongodb'
+ optional 'org.springframework.data:spring-data-neo4j'
+ optional 'org.springframework.data:spring-data-redis'
+ optional 'org.springframework.data:spring-data-solr'
+ optional 'org.springframework.integration:spring-integration-core'
+ optional 'org.springframework.security:spring-security-config'
+ optional 'org.springframework.security:spring-security-web'
+ optional 'org.springframework.session:spring-session-core'
+ optional 'redis.clients:jedis'
+
+ testImplementation project(':spring-boot-project:spring-boot-test')
+ testImplementation project(':spring-boot-project:spring-boot-tools:spring-boot-test-support')
+ testImplementation 'io.projectreactor:reactor-test'
+ testImplementation 'com.squareup.okhttp3:mockwebserver'
+ testImplementation 'com.jayway.jsonpath:json-path'
+ testImplementation 'io.undertow:undertow-core'
+ testImplementation ('io.undertow:undertow-servlet') {
+ exclude group: 'org.jboss.spec.javax.annotation', module: 'jboss-annotations-api_1.2_spec'
+ exclude group: 'org.jboss.spec.javax.servlet', module: 'jboss-servlet-api_4.0_spec'
+ }
+ testImplementation 'javax.xml.bind:jaxb-api'
+ testImplementation 'org.apache.logging.log4j:log4j-to-slf4j'
+ testImplementation 'org.aspectj:aspectjrt'
+ testImplementation 'org.assertj:assertj-core'
+ testImplementation 'org.eclipse.jetty:jetty-webapp'
+ testImplementation 'org.glassfish.jersey.ext:jersey-spring4'
+ testImplementation 'org.glassfish.jersey.media:jersey-media-json-jackson'
+ testImplementation 'org.hamcrest:hamcrest'
+ testImplementation 'org.hsqldb:hsqldb'
+ testImplementation 'org.junit.jupiter:junit-jupiter'
+ testImplementation 'org.mockito:mockito-core'
+ testImplementation 'org.skyscreamer:jsonassert'
+ testImplementation 'org.springframework:spring-orm'
+ testImplementation 'org.springframework.data:spring-data-elasticsearch'
+ testImplementation 'org.springframework.data:spring-data-rest-webmvc'
+ testImplementation 'org.springframework.integration:spring-integration-jmx'
+ testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
+ testImplementation 'org.springframework.restdocs:spring-restdocs-webtestclient'
+ testImplementation 'org.springframework.security:spring-security-test'
+ testImplementation 'org.yaml:snakeyaml'
+
+ testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
+ testRuntimeOnly 'org.springframework.security:spring-security-oauth2-jose'
+ testRuntimeOnly 'org.springframework.security:spring-security-oauth2-resource-server'
+}
+
+compileJava {
+ options.compilerArgs << '-parameters'
+}
+
+compileTestJava {
+ options.compilerArgs << '-parameters'
+}
+
+test {
+ outputs.dir("$buildDir/generated-snippets")
+}
+
+asciidoctor {
+ configurations 'asciidoctorExtensions'
+ dependsOn test
+ sources {
+ include 'index.adoc'
+ }
+}
+
+asciidoctorPdf {
+ configurations 'asciidoctorExtensions'
+ dependsOn test
+ sources {
+ include 'index.adoc'
+ }
+}
+
+task zip(type: Zip) {
+ dependsOn asciidoctor, asciidoctorPdf
+ duplicatesStrategy 'fail'
+ from(asciidoctorPdf.outputDir) {
+ into 'pdf'
+ }
+ from(asciidoctor.outputDir) {
+ into 'html'
+ }
+}
+
+artifacts {
+ documentation zip
+}
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml b/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml
deleted file mode 100644
index aa824b2ae10..00000000000
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml
+++ /dev/null
@@ -1,863 +0,0 @@
-
-
- 4.0.0
-
- org.springframework.boot
- spring-boot-parent
- ${revision}
- ../spring-boot-parent
-
- spring-boot-actuator-autoconfigure
- Spring Boot Actuator AutoConfigure
- Spring Boot Actuator AutoConfigure
-
- ${basedir}/../..
- ${project.build.directory}/refdocs/
-
-
- ${git.url}
- ${git.connection}
- ${git.developerConnection}
-
-
-
-
- org.springframework.boot
- spring-boot-actuator
-
-
- org.springframework.boot
- spring-boot-autoconfigure
-
-
- com.fasterxml.jackson.core
- jackson-databind
-
-
- org.springframework
- spring-core
-
-
- org.springframework
- spring-context
-
-
-
- ch.qos.logback
- logback-classic
- true
-
-
- com.fasterxml.jackson.dataformat
- jackson-dataformat-xml
- true
-
-
- com.github.ben-manes.caffeine
- caffeine
- true
-
-
- com.hazelcast
- hazelcast
- true
-
-
- com.hazelcast
- hazelcast-spring
- true
-
-
- com.sun.mail
- jakarta.mail
- true
-
-
- com.zaxxer
- HikariCP
- true
-
-
- io.dropwizard.metrics
- metrics-jmx
- true
-
-
- io.lettuce
- lettuce-core
- true
-
-
- io.micrometer
- micrometer-core
- true
-
-
- io.micrometer
- micrometer-jersey2
- true
-
-
- io.micrometer
- micrometer-registry-appoptics
- true
-
-
- io.micrometer
- micrometer-registry-atlas
- true
-
-
- io.micrometer
- micrometer-registry-datadog
- true
-
-
- io.micrometer
- micrometer-registry-dynatrace
- true
-
-
- io.micrometer
- micrometer-registry-elastic
- true
-
-
- io.micrometer
- micrometer-registry-ganglia
- true
-
-
- io.micrometer
- micrometer-registry-graphite
- true
-
-
- io.micrometer
- micrometer-registry-humio
- true
-
-
- io.micrometer
- micrometer-registry-influx
- true
-
-
- io.micrometer
- micrometer-registry-jmx
- true
-
-
- io.micrometer
- micrometer-registry-kairos
- true
-
-
- io.micrometer
- micrometer-registry-new-relic
- true
-
-
- io.micrometer
- micrometer-registry-prometheus
- true
-
-
- io.prometheus
- simpleclient_pushgateway
- true
-
-
- io.micrometer
- micrometer-registry-signalfx
- true
-
-
- io.micrometer
- micrometer-registry-stackdriver
- true
-
-
- io.micrometer
- micrometer-registry-statsd
- true
-
-
- io.micrometer
- micrometer-registry-wavefront
- true
-
-
- io.projectreactor.netty
- reactor-netty
- true
-
-
- io.searchbox
- jest
- true
-
-
- jakarta.jms
- jakarta.jms-api
- true
-
-
- jakarta.servlet
- jakarta.servlet-api
- true
-
-
- jakarta.persistence
- jakarta.persistence-api
- true
-
-
- jakarta.ws.rs
- jakarta.ws.rs-api
- true
-
-
- javax.cache
- cache-api
- true
-
-
- net.sf.ehcache
- ehcache
- true
-
-
- org.apache.activemq
- activemq-broker
- true
-
-
- geronimo-jms_1.1_spec
- org.apache.geronimo.specs
-
-
-
-
- org.apache.commons
- commons-dbcp2
- true
-
-
- org.apache.kafka
- kafka-clients
- true
-
-
- org.apache.tomcat.embed
- tomcat-embed-core
- true
-
-
- org.apache.tomcat.embed
- tomcat-embed-el
- true
-
-
- org.apache.tomcat
- tomcat-jdbc
- true
-
-
- org.aspectj
- aspectjweaver
- true
-
-
- org.eclipse.jetty
- jetty-server
- true
-
-
- javax.servlet
- javax.servlet-api
-
-
-
-
- org.elasticsearch
- elasticsearch
- true
-
-
- org.elasticsearch.client
- elasticsearch-rest-client
- true
-
-
- org.flywaydb
- flyway-core
- true
-
-
- org.glassfish.jersey.core
- jersey-server
- true
-
-
- javax.validation
- validation-api
-
-
-
-
- org.glassfish.jersey.containers
- jersey-container-servlet-core
- true
-
-
- org.hibernate
- hibernate-core
- true
-
-
- javax.activation
- javax.activation-api
-
-
- javax.xml.bind
- jaxb-api
-
-
- javax.persistence
- javax.persistence-api
-
-
-
-
- org.hibernate.validator
- hibernate-validator
- true
-
-
- javax.validation
- validation-api
-
-
-
-
- org.influxdb
- influxdb-java
- true
-
-
- org.jolokia
- jolokia-core
- true
-
-
- org.infinispan
- infinispan-spring5-embedded
- true
-
-
- org.liquibase
- liquibase-core
- true
-
-
- org.mongodb
- mongodb-driver-async
- true
-
-
- org.mongodb
- mongodb-driver-reactivestreams
- true
-
-
- org.springframework
- spring-jdbc
- true
-
-
- org.springframework
- spring-jms
- true
-
-
- org.springframework
- spring-messaging
- true
-
-
- org.springframework
- spring-webflux
- true
-
-
- org.springframework
- spring-webmvc
- true
-
-
- org.springframework.amqp
- spring-rabbit
- true
-
-
- org.springframework.data
- spring-data-cassandra
- true
-
-
- org.springframework.data
- spring-data-couchbase
- true
-
-
- org.springframework.data
- spring-data-ldap
- true
-
-
- org.springframework.data
- spring-data-mongodb
- true
-
-
- org.springframework.data
- spring-data-neo4j
- true
-
-
- org.springframework.data
- spring-data-redis
- true
-
-
- org.springframework.data
- spring-data-solr
- true
-
-
-
- wstx-asl
- org.codehaus.woodstox
-
-
-
-
- org.springframework.integration
- spring-integration-core
- true
-
-
- org.springframework.security
- spring-security-config
- true
-
-
- org.springframework.security
- spring-security-web
- true
-
-
- org.springframework.session
- spring-session-core
- true
-
-
-
- com.fasterxml.jackson.datatype
- jackson-datatype-jsr310
- runtime
-
-
-
- org.springframework.boot
- spring-boot-autoconfigure-processor
- true
-
-
- org.springframework.boot
- spring-boot-configuration-processor
- true
-
-
-
- org.springframework.boot
- spring-boot-test
- test
-
-
- org.springframework.boot
- spring-boot-test-support
- test
-
-
- io.projectreactor
- reactor-test
- test
-
-
- com.squareup.okhttp3
- mockwebserver
- test
-
-
- org.hamcrest
- hamcrest-core
-
-
-
-
- com.jayway.jsonpath
- json-path
- test
-
-
- io.undertow
- undertow-core
- test
-
-
- io.undertow
- undertow-servlet
- test
-
-
- org.jboss.spec.javax.servlet
- jboss-servlet-api_3.1_spec
-
-
-
-
- jakarta.validation
- jakarta.validation-api
- test
-
-
- jakarta.xml.bind
- jakarta.xml.bind-api
- test
-
-
- org.apache.logging.log4j
- log4j-to-slf4j
- test
-
-
- org.aspectj
- aspectjrt
- test
-
-
- org.eclipse.jetty
- jetty-webapp
- test
-
-
- org.hsqldb
- hsqldb
- test
-
-
- org.glassfish.jersey.ext
- jersey-spring5
- test
-
-
- org.glassfish.jersey.media
- jersey-media-json-jackson
- test
-
-
- org.skyscreamer
- jsonassert
- test
-
-
- org.springframework
- spring-orm
- test
-
-
- org.springframework.data
- spring-data-elasticsearch
- test
-
-
- org.springframework.data
- spring-data-rest-webmvc
- test
-
-
- org.springframework.integration
- spring-integration-jmx
- test
-
-
- org.springframework.restdocs
- spring-restdocs-mockmvc
- test
-
-
- javax.servlet
- javax.servlet-api
-
-
-
-
- org.springframework.restdocs
- spring-restdocs-webtestclient
- test
-
-
- org.springframework.security
- spring-security-test
- test
-
-
- org.springframework.security
- spring-security-oauth2-resource-server
- test
-
-
- org.springframework.security
- spring-security-oauth2-jose
- test
-
-
- org.yaml
- snakeyaml
- test
-
-
- redis.clients
- jedis
- true
-
-
-
-
- full
-
-
- full
-
-
-
-
-
- com.googlecode.maven-download-plugin
- download-maven-plugin
-
-
- unpack-doc-resources
- generate-resources
-
- wget
-
-
- ${spring-doc-resources.url}
- true
- ${refdocs.build.directory}
-
-
-
-
-
- org.apache.maven.plugins
- maven-antrun-plugin
-
-
- ant-contrib
- ant-contrib
- 1.0b3
-
-
- ant
- ant
-
-
-
-
- org.apache.ant
- ant-nodeps
- 1.8.1
-
-
- org.tigris.antelope
- antelopetasks
- 3.2.10
-
-
-
-
- set-up-maven-properties
- prepare-package
-
- run
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- package-docs-zip
- package
-
- run
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- org.asciidoctor
- asciidoctor-maven-plugin
-
-
- generate-html-documentation
- prepare-package
-
- process-asciidoc
-
-
- html5
- ${project.build.directory}/generated-docs/reference/html
- highlight.js
- book
-
- js/highlight
- github
- true
- ./images
- font
- css/
- spring.css
- warn
-
-
- true
-
- DEBUG
-
-
-
-
-
- generate-pdf-documentation
- prepare-package
-
- process-asciidoc
-
-
- pdf
- ${project.build.directory}/generated-docs/reference/pdf
-
-
-
-
- ${refdocs.build.directory}
- index.adoc
-
- ${version-type}
- ${project.version}
- ${project.build.directory}/generated-snippets/
-
-
-
-
- org.asciidoctor
- asciidoctorj-pdf
- 1.5.0-alpha.18
-
-
-
-
- org.apache.maven.plugins
- maven-resources-plugin
-
-
- copy-asciidoc-resources
- generate-resources
-
- copy-resources
-
-
- ${refdocs.build.directory}
-
-
- src/main/asciidoc
- false
-
-
-
-
-
-
-
- org.codehaus.mojo
- build-helper-maven-plugin
-
-
- attach-zip
-
- attach-artifact
-
-
-
-
- ${project.build.directory}/${project.artifactId}-${project.version}-docs.zip
- zip
- docs
-
-
-
-
-
-
-
-
-
-
-
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/auditevents.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/auditevents.adoc
similarity index 79%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/auditevents.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/auditevents.adoc
index 91dac834d5e..9c6358f20b3 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/auditevents.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/auditevents.adoc
@@ -10,12 +10,12 @@ The `auditevents` endpoint provides information about the application's audit ev
To retrieve the audit events, make a `GET` request to `/actuator/auditevents`, as shown in the following curl-based example:
-include::{snippets}auditevents/filtered/curl-request.adoc[]
+include::{snippets}/auditevents/filtered/curl-request.adoc[]
The preceding example retrieves `logout` events for the principal, `alice`, that occurred after 09:37 on 7 November 2017 in the UTC timezone.
The resulting response is similar to the following:
-include::{snippets}auditevents/filtered/http-response.adoc[]
+include::{snippets}/auditevents/filtered/http-response.adoc[]
@@ -26,7 +26,7 @@ The endpoint uses query parameters to limit the events that it returns.
The following table shows the supported query parameters:
[cols="2,4"]
-include::{snippets}auditevents/filtered/request-parameters.adoc[]
+include::{snippets}/auditevents/filtered/request-parameters.adoc[]
@@ -37,4 +37,4 @@ The response contains details of all of the audit events that matched the query.
The following table describes the structure of the response:
[cols="2,1,3"]
-include::{snippets}auditevents/all/response-fields.adoc[]
+include::{snippets}/auditevents/all/response-fields.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/beans.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/beans.adoc
similarity index 78%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/beans.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/beans.adoc
index 5417a906011..e3307538bae 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/beans.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/beans.adoc
@@ -10,11 +10,11 @@ The `beans` endpoint provides information about the application's beans.
To retrieve the beans, make a `GET` request to `/actuator/beans`, as shown in the following curl-based example:
-include::{snippets}beans/curl-request.adoc[]
+include::{snippets}/beans/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}beans/http-response.adoc[]
+include::{snippets}/beans/http-response.adoc[]
@@ -25,4 +25,4 @@ The response contains details of the application's beans.
The following table describes the structure of the response:
[cols="2,1,3"]
-include::{snippets}beans/response-fields.adoc[]
+include::{snippets}/beans/response-fields.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/caches.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/caches.adoc
similarity index 78%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/caches.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/caches.adoc
index 3f2156a4b9a..f6057a656d2 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/caches.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/caches.adoc
@@ -9,11 +9,11 @@ The `caches` endpoint provides access to the application's caches.
== Retrieving All Caches
To retrieve the application's caches, make a `GET` request to `/actuator/caches`, as shown in the following curl-based example:
-include::{snippets}caches/all/curl-request.adoc[]
+include::{snippets}/caches/all/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}caches/all/http-response.adoc[]
+include::{snippets}/caches/all/http-response.adoc[]
@@ -23,7 +23,7 @@ The response contains details of the application's caches.
The following table describes the structure of the response:
[cols="3,1,3"]
-include::{snippets}caches/all/response-fields.adoc[]
+include::{snippets}/caches/all/response-fields.adoc[]
@@ -31,12 +31,12 @@ include::{snippets}caches/all/response-fields.adoc[]
== Retrieving Caches by Name
To retrieve a cache by name, make a `GET` request to `/actuator/caches/\{name}`, as shown in the following curl-based example:
-include::{snippets}caches/named/curl-request.adoc[]
+include::{snippets}/caches/named/curl-request.adoc[]
The preceding example retrieves information about the cache named `cities`.
The resulting response is similar to the following:
-include::{snippets}caches/named/http-response.adoc[]
+include::{snippets}/caches/named/http-response.adoc[]
@@ -47,7 +47,7 @@ Otherwise, the `cacheManager` must be specified.
The following table shows the supported query parameters:
[cols="2,4"]
-include::{snippets}caches/named/request-parameters.adoc[]
+include::{snippets}/caches/named/request-parameters.adoc[]
@@ -57,7 +57,7 @@ The response contains details of the requested cache.
The following table describes the structure of the response:
[cols="3,1,3"]
-include::{snippets}caches/named/response-fields.adoc[]
+include::{snippets}/caches/named/response-fields.adoc[]
@@ -65,7 +65,7 @@ include::{snippets}caches/named/response-fields.adoc[]
== Evict All Caches
To clear all available caches, make a `DELETE` request to `/actuator/caches` as shown in the following curl-based example:
-include::{snippets}caches/evict-all/curl-request.adoc[]
+include::{snippets}/caches/evict-all/curl-request.adoc[]
@@ -73,7 +73,7 @@ include::{snippets}caches/evict-all/curl-request.adoc[]
== Evict a Cache by Name
To evict a particular cache, make a `DELETE` request to `/actuator/caches/\{name}` as shown in the following curl-based example:
-include::{snippets}caches/evict-named/curl-request.adoc[]
+include::{snippets}/caches/evict-named/curl-request.adoc[]
NOTE: As there are two caches named `countries`, the `cacheManager` has to be provided to specify which `Cache` should be cleared.
@@ -86,4 +86,4 @@ Otherwise, the `cacheManager` must be specified.
The following table shows the supported query parameters:
[cols="2,4"]
-include::{snippets}caches/evict-named/request-parameters.adoc[]
+include::{snippets}/caches/evict-named/request-parameters.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/conditions.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/conditions.adoc
similarity index 80%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/conditions.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/conditions.adoc
index 26dace0267f..2477015d832 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/conditions.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/conditions.adoc
@@ -10,11 +10,11 @@ The `conditions` endpoint provides information about the evaluation of condition
To retrieve the report, make a `GET` request to `/actuator/conditions`, as shown in the following curl-based example:
-include::{snippets}conditions/curl-request.adoc[]
+include::{snippets}/conditions/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}conditions/http-response.adoc[]
+include::{snippets}/conditions/http-response.adoc[]
@@ -25,4 +25,4 @@ The response contains details of the application's condition evaluation.
The following table describes the structure of the response:
[cols="3,1,3"]
-include::{snippets}conditions/response-fields.adoc[]
+include::{snippets}/conditions/response-fields.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/configprops.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/configprops.adoc
similarity index 80%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/configprops.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/configprops.adoc
index 651b0ec5123..1f251b9c2ed 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/configprops.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/configprops.adoc
@@ -10,11 +10,11 @@ The `configprops` endpoint provides information about the application's `@Config
To retrieve the `@ConfigurationProperties` beans, make a `GET` request to `/actuator/configprops`, as shown in the following curl-based example:
-include::{snippets}configprops/curl-request.adoc[]
+include::{snippets}/configprops/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}configprops/http-response.adoc[]
+include::{snippets}/configprops/http-response.adoc[]
@@ -25,4 +25,4 @@ The response contains details of the application's `@ConfigurationProperties` be
The following table describes the structure of the response:
[cols="2,1,3"]
-include::{snippets}configprops/response-fields.adoc[]
+include::{snippets}/configprops/response-fields.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/env.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/env.adoc
similarity index 78%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/env.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/env.adoc
index d7b917ca166..4b624946dba 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/env.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/env.adoc
@@ -10,11 +10,11 @@ The `env` endpoint provides information about the application's `Environment`.
To retrieve the entire environment, make a `GET` request to `/actuator/env`, as shown in the following curl-based example:
-include::{snippets}env/all/curl-request.adoc[]
+include::{snippets}/env/all/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}env/all/http-response.adoc[]
+include::{snippets}/env/all/http-response.adoc[]
@@ -25,7 +25,7 @@ The response contains details of the application's `Environment`.
The following table describes the structure of the response:
[cols="3,1,3"]
-include::{snippets}env/all/response-fields.adoc[]
+include::{snippets}/env/all/response-fields.adoc[]
@@ -34,12 +34,12 @@ include::{snippets}env/all/response-fields.adoc[]
To retrieve a single property, make a `GET` request to `/actuator/env/{property.name}`, as shown in the following curl-based example:
-include::{snippets}env/single/curl-request.adoc[]
+include::{snippets}/env/single/curl-request.adoc[]
The preceding example retrieves information about the property named `com.example.cache.max-size`.
The resulting response is similar to the following:
-include::{snippets}env/single/http-response.adoc[]
+include::{snippets}/env/single/http-response.adoc[]
@@ -50,4 +50,4 @@ The response contains details of the requested property.
The following table describes the structure of the response:
[cols="3,1,3"]
-include::{snippets}env/single/response-fields.adoc[]
+include::{snippets}/env/single/response-fields.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/flyway.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/flyway.adoc
similarity index 79%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/flyway.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/flyway.adoc
index 8fe81d6295d..d70b0e6a563 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/flyway.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/flyway.adoc
@@ -10,11 +10,11 @@ The `flyway` endpoint provides information about database migrations performed b
To retrieve the migrations, make a `GET` request to `/actuator/flyway`, as shown in the following curl-based example:
-include::{snippets}flyway/curl-request.adoc[]
+include::{snippets}/flyway/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}flyway/http-response.adoc[]
+include::{snippets}/flyway/http-response.adoc[]
@@ -25,4 +25,4 @@ The response contains details of the application's Flyway migrations.
The following table describes the structure of the response:
[cols="2,1,3"]
-include::{snippets}flyway/response-fields.adoc[]
+include::{snippets}/flyway/response-fields.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/health.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/health.adoc
similarity index 82%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/health.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/health.adoc
index a7c1998dde3..6cbaf51771b 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/health.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/health.adoc
@@ -8,11 +8,11 @@ The `health` endpoint provides detailed information about the health of the appl
== Retrieving the Health of the application
To retrieve the health of the application, make a `GET` request to `/actuator/health`, as shown in the following curl-based example:
-include::{snippets}health/curl-request.adoc[]
+include::{snippets}/health/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}health/http-response.adoc[]
+include::{snippets}/health/http-response.adoc[]
@@ -22,7 +22,7 @@ The response contains details of the health of the application.
The following table describes the structure of the response:
[cols="2,1,3"]
-include::{snippets}health/response-fields.adoc[]
+include::{snippets}/health/response-fields.adoc[]
NOTE: The response fields above are for the V3 API.
If you need to return V2 JSON you should use an accept header or `application/vnd.spring-boot.actuator.v2+json`
@@ -33,11 +33,11 @@ If you need to return V2 JSON you should use an accept header or `application/vn
== Retrieving the Health of a component
To retrieve the health of a particular component of the application's health, make a `GET` request to `/actuator/health/\{component}`, as shown in the following curl-based example:
-include::{snippets}health/component/curl-request.adoc[]
+include::{snippets}/health/component/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}health/component/http-response.adoc[]
+include::{snippets}/health/component/http-response.adoc[]
@@ -47,7 +47,7 @@ The response contains details of the health of a particular component of the app
The following table describes the structure of the response:
[cols="2,1,3"]
-include::{snippets}health/component/response-fields.adoc[]
+include::{snippets}/health/component/response-fields.adoc[]
@@ -55,11 +55,11 @@ include::{snippets}health/component/response-fields.adoc[]
== Retrieving the Health of a nested component
If a particular component contains other nested components (as the `broker` indicator in the example above), the health of such a nested component can be retrieved by issuing a `GET` request to `/actuator/health/\{component}/\{subcomponent}`, as shown in the following curl-based example:
-include::{snippets}health/instance/curl-request.adoc[]
+include::{snippets}/health/instance/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}health/instance/http-response.adoc[]
+include::{snippets}/health/instance/http-response.adoc[]
Components of an application's health may be nested arbitrarily deep depending on the application's health indicators and how they have been grouped.
The health endpoint supports any number of `/\{component}` identifiers in the URL to allow the health of a component at any depth to be retrieved.
@@ -72,4 +72,4 @@ The response contains details of the health of an instance of a particular compo
The following table describes the structure of the response:
[cols="2,1,3"]
-include::{snippets}health/instance/response-fields.adoc[]
+include::{snippets}/health/instance/response-fields.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/heapdump.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/heapdump.adoc
similarity index 92%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/heapdump.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/heapdump.adoc
index ebbba69207d..57488cb0170 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/heapdump.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/heapdump.adoc
@@ -13,6 +13,6 @@ The response is binary data in https://docs.oracle.com/javase/8/docs/technotes/s
Typically, you should save the response to disk for subsequent analysis.
When using curl, this can be achieved by using the `-O` option, as shown in the following example:
-include::{snippets}heapdump/curl-request.adoc[]
+include::{snippets}/heapdump/curl-request.adoc[]
The preceding example results in a file named `heapdump` being written to the current working directory.
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/httptrace.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/httptrace.adoc
similarity index 79%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/httptrace.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/httptrace.adoc
index ea1149f9efd..b7937e57a2b 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/httptrace.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/httptrace.adoc
@@ -10,11 +10,11 @@ The `httptrace` endpoint provides information about HTTP request-response exchan
To retrieve the traces, make a `GET` request to `/actuator/httptrace`, as shown in the following curl-based example:
-include::{snippets}httptrace/curl-request.adoc[]
+include::{snippets}/httptrace/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}httptrace/http-response.adoc[]
+include::{snippets}/httptrace/http-response.adoc[]
@@ -25,4 +25,4 @@ The response contains details of the traced HTTP request-response exchanges.
The following table describes the structure of the response:
[cols="2,1,3"]
-include::{snippets}httptrace/response-fields.adoc[]
+include::{snippets}/httptrace/response-fields.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/info.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/info.adoc
similarity index 81%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/info.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/info.adoc
index f431c3a8d80..91f884face2 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/info.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/info.adoc
@@ -10,11 +10,11 @@ The `info` endpoint provides general information about the application.
To retrieve the information about the application, make a `GET` request to `/actuator/info`, as shown in the following curl-based example:
-include::{snippets}info/curl-request.adoc[]
+include::{snippets}/info/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}info/http-response.adoc[]
+include::{snippets}/info/http-response.adoc[]
@@ -33,7 +33,7 @@ Spring Boot provides `build` and `git` contributions.
The following table describe the structure of the `build` section of the response:
[cols="2,1,3"]
-include::{snippets}info/response-fields-beneath-build.adoc[]
+include::{snippets}/info/response-fields-beneath-build.adoc[]
@@ -43,4 +43,4 @@ include::{snippets}info/response-fields-beneath-build.adoc[]
The following table describes the structure of the `git` section of the response:
[cols="2,1,3"]
-include::{snippets}info/response-fields-beneath-git.adoc[]
+include::{snippets}/info/response-fields-beneath-git.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/integrationgraph.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/integrationgraph.adoc
similarity index 80%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/integrationgraph.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/integrationgraph.adoc
index e2d3274729d..5a2b28a63a0 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/integrationgraph.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/integrationgraph.adoc
@@ -9,11 +9,11 @@ The `integrationgraph` endpoint exposes a graph containing all Spring Integratio
== Retrieving the Spring Integration graph
To retrieve the information about the application, make a `GET` request to `/actuator/integrationgraph`, as shown in the following curl-based example:
-include::{snippets}integrationgraph/graph/curl-request.adoc[]
+include::{snippets}/integrationgraph/graph/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}integrationgraph/graph/http-response.adoc[]
+include::{snippets}/integrationgraph/graph/http-response.adoc[]
@@ -28,8 +28,8 @@ More information about the structure can be found in the https://docs.spring.io/
== Rebuilding the Spring Integration graph
To rebuild the exposed graph, make a `POST` request to `/actuator/integrationgraph`, as shown in the following curl-based example:
-include::{snippets}integrationgraph/rebuild/curl-request.adoc[]
+include::{snippets}/integrationgraph/rebuild/curl-request.adoc[]
This will result in a `204 - No Content` response:
-include::{snippets}integrationgraph/rebuild/http-response.adoc[]
+include::{snippets}/integrationgraph/rebuild/http-response.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/liquibase.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/liquibase.adoc
similarity index 79%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/liquibase.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/liquibase.adoc
index 1e84c8efac8..a517d85cce2 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/liquibase.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/liquibase.adoc
@@ -10,11 +10,11 @@ The `liquibase` endpoint provides information about database change sets applied
To retrieve the changes, make a `GET` request to `/actuator/liquibase`, as shown in the following curl-based example:
-include::{snippets}liquibase/curl-request.adoc[]
+include::{snippets}/liquibase/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}liquibase/http-response.adoc[]
+include::{snippets}/liquibase/http-response.adoc[]
@@ -25,4 +25,4 @@ The response contains details of the application's Liquibase change sets.
The following table describes the structure of the response:
[cols="2,1,3"]
-include::{snippets}liquibase/response-fields.adoc[]
+include::{snippets}/liquibase/response-fields.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/logfile.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/logfile.adoc
similarity index 77%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/logfile.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/logfile.adoc
index 99121fc6734..9fd1dc8658f 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/logfile.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/logfile.adoc
@@ -10,11 +10,11 @@ The `logfile` endpoint provides access to the contents of the application's log
To retrieve the log file, make a `GET` request to `/actuator/logfile`, as shown in the following curl-based example:
-include::{snippets}logfile/entire/curl-request.adoc[]
+include::{snippets}/logfile/entire/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}logfile/entire/http-response.adoc[]
+include::{snippets}/logfile/entire/http-response.adoc[]
@@ -25,9 +25,9 @@ NOTE: Retrieving part of the log file is not supported when using Jersey.
To retrieve part of the log file, make a `GET` request to `/actuator/logfile` by using the `Range` header, as shown in the following curl-based example:
-include::{snippets}logfile/range/curl-request.adoc[]
+include::{snippets}/logfile/range/curl-request.adoc[]
The preceding example retrieves the first 1024 bytes of the log file.
The resulting response is similar to the following:
-include::{snippets}logfile/range/http-response.adoc[]
+include::{snippets}/logfile/range/http-response.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/loggers.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/loggers.adoc
similarity index 79%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/loggers.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/loggers.adoc
index e4609c73e7f..5e88b104e45 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/loggers.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/loggers.adoc
@@ -10,11 +10,11 @@ The `loggers` endpoint provides access to the application's loggers and the conf
To retrieve the application's loggers, make a `GET` request to `/actuator/loggers`, as shown in the following curl-based example:
-include::{snippets}loggers/all/curl-request.adoc[]
+include::{snippets}/loggers/all/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}loggers/all/http-response.adoc[]
+include::{snippets}/loggers/all/http-response.adoc[]
@@ -25,7 +25,7 @@ The response contains details of the application's loggers.
The following table describes the structure of the response:
[cols="3,1,3"]
-include::{snippets}loggers/all/response-fields.adoc[]
+include::{snippets}/loggers/all/response-fields.adoc[]
@@ -34,12 +34,12 @@ include::{snippets}loggers/all/response-fields.adoc[]
To retrieve a single logger, make a `GET` request to `/actuator/loggers/{logger.name}`, as shown in the following curl-based example:
-include::{snippets}loggers/single/curl-request.adoc[]
+include::{snippets}/loggers/single/curl-request.adoc[]
The preceding example retrieves information about the logger named `com.example`.
The resulting response is similar to the following:
-include::{snippets}loggers/single/http-response.adoc[]
+include::{snippets}/loggers/single/http-response.adoc[]
@@ -50,7 +50,7 @@ The response contains details of the requested logger.
The following table describes the structure of the response:
[cols="3,1,3"]
-include::{snippets}loggers/single/response-fields.adoc[]
+include::{snippets}/loggers/single/response-fields.adoc[]
@@ -60,12 +60,12 @@ include::{snippets}loggers/single/response-fields.adoc[]
To retrieve a single group, make a `GET` request to `/actuator/loggers/{group.name}`,
as shown in the following curl-based example:
-include::{snippets}loggers/group/curl-request.adoc[]
+include::{snippets}/loggers/group/curl-request.adoc[]
The preceding example retrieves information about the logger group named `test`.
The resulting response is similar to the following:
-include::{snippets}loggers/group/http-response.adoc[]
+include::{snippets}/loggers/group/http-response.adoc[]
@@ -76,7 +76,7 @@ The response contains details of the requested group.
The following table describes the structure of the response:
[cols="3,1,3"]
-include::{snippets}loggers/group/response-fields.adoc[]
+include::{snippets}/loggers/group/response-fields.adoc[]
@@ -85,7 +85,7 @@ include::{snippets}loggers/group/response-fields.adoc[]
To set the level of a logger, make a `POST` request to `/actuator/loggers/{logger.name}` with a JSON body that specifies the configured level for the logger, as shown in the following curl-based example:
-include::{snippets}loggers/set/curl-request.adoc[]
+include::{snippets}/loggers/set/curl-request.adoc[]
The preceding example sets the `configuredLevel` of the `com.example` logger to `DEBUG`.
@@ -98,7 +98,7 @@ The request specifies the desired level of the logger.
The following table describes the structure of the request:
[cols="3,1,3"]
-include::{snippets}loggers/set/request-fields.adoc[]
+include::{snippets}/loggers/set/request-fields.adoc[]
@@ -107,7 +107,7 @@ include::{snippets}loggers/set/request-fields.adoc[]
To set the level of a logger, make a `POST` request to `/actuator/loggers/{group.name}` with a JSON body that specifies the configured level for the logger group, as shown in the following curl-based example:
-include::{snippets}loggers/setGroup/curl-request.adoc[]
+include::{snippets}/loggers/setGroup/curl-request.adoc[]
The preceding example sets the `configuredLevel` of the `test` logger group to `DEBUG`.
@@ -120,7 +120,7 @@ The request specifies the desired level of the logger group.
The following table describes the structure of the request:
[cols="3,1,3"]
-include::{snippets}loggers/set/request-fields.adoc[]
+include::{snippets}/loggers/set/request-fields.adoc[]
@@ -129,6 +129,6 @@ include::{snippets}loggers/set/request-fields.adoc[]
To clear the level of a logger, make a `POST` request to `/actuator/loggers/{logger.name}` with a JSON body containing an empty object, as shown in the following curl-based example:
-include::{snippets}loggers/clear/curl-request.adoc[]
+include::{snippets}/loggers/clear/curl-request.adoc[]
The preceding example clears the configured level of the `com.example` logger.
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/mappings.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/mappings.adoc
similarity index 83%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/mappings.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/mappings.adoc
index 1d7371d9e25..20ac03e0f78 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/mappings.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/mappings.adoc
@@ -10,11 +10,11 @@ The `mappings` endpoint provides information about the application's request map
To retrieve the mappings, make a `GET` request to `/actuator/mappings`, as shown in the following curl-based example:
-include::{snippets}mappings/curl-request.adoc[]
+include::{snippets}/mappings/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}mappings/http-response.adoc[]
+include::{snippets}/mappings/http-response.adoc[]
@@ -26,7 +26,7 @@ The items found in the response depend on the type of web application (reactive
The following table describes the structure of the common elements of the response:
[cols="2,1,3"]
-include::{snippets}mappings/response-fields.adoc[]
+include::{snippets}/mappings/response-fields.adoc[]
The entries that may be found in `contexts.*.mappings` are described in the following sections.
@@ -38,7 +38,7 @@ When using Spring MVC, the response contains details of any `DispatcherServlet`
The following table describes the structure of this section of the response:
[cols="4,1,2"]
-include::{snippets}mappings/response-fields-dispatcher-servlets.adoc[]
+include::{snippets}/mappings/response-fields-dispatcher-servlets.adoc[]
@@ -49,7 +49,7 @@ When using the Servlet stack, the response contains details of any `Servlet` map
The following table describes the structure of this section of the response:
[cols="2,1,3"]
-include::{snippets}mappings/response-fields-servlets.adoc[]
+include::{snippets}/mappings/response-fields-servlets.adoc[]
@@ -60,7 +60,7 @@ When using the Servlet stack, the response contains details of any `Filter` mapp
The following table describes the structure of this section of the response:
[cols="2,1,3"]
-include::{snippets}mappings/response-fields-servlet-filters.adoc[]
+include::{snippets}/mappings/response-fields-servlet-filters.adoc[]
@@ -71,4 +71,4 @@ When using Spring WebFlux, the response contains details of any `DispatcherHandl
The following table describes the structure of this section of the response:
[cols="4,1,2"]
-include::{snippets}mappings/response-fields-dispatcher-handlers.adoc[]
+include::{snippets}/mappings/response-fields-dispatcher-handlers.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/metrics.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/metrics.adoc
similarity index 76%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/metrics.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/metrics.adoc
index db26c3fd95e..ac5430abe8f 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/metrics.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/metrics.adoc
@@ -10,11 +10,11 @@ The `metrics` endpoint provides access to application metrics.
To retrieve the names of the available metrics, make a `GET` request to `/actuator/metrics`, as shown in the following curl-based example:
-include::{snippets}metrics/names/curl-request.adoc[]
+include::{snippets}/metrics/names/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}metrics/names/http-response.adoc[]
+include::{snippets}/metrics/names/http-response.adoc[]
@@ -25,7 +25,7 @@ The response contains details of the metric names.
The following table describes the structure of the response:
[cols="3,1,2"]
-include::{snippets}metrics/names/response-fields.adoc[]
+include::{snippets}/metrics/names/response-fields.adoc[]
@@ -34,12 +34,12 @@ include::{snippets}metrics/names/response-fields.adoc[]
To retrieve a metric, make a `GET` request to `/actuator/metrics/{metric.name}`, as shown in the following curl-based example:
-include::{snippets}metrics/metric/curl-request.adoc[]
+include::{snippets}/metrics/metric/curl-request.adoc[]
The preceding example retrieves information about the metric named `jvm.memory.max`.
The resulting response is similar to the following:
-include::{snippets}metrics/metric/http-response.adoc[]
+include::{snippets}/metrics/metric/http-response.adoc[]
@@ -50,7 +50,7 @@ The endpoint uses query parameters to <> into
The following table shows the single supported query parameter:
[cols="2,4"]
-include::{snippets}metrics/metric-with-tags/request-parameters.adoc[]
+include::{snippets}/metrics/metric-with-tags/request-parameters.adoc[]
@@ -60,7 +60,7 @@ include::{snippets}metrics/metric-with-tags/request-parameters.adoc[]
The response contains details of the metric.
The following table describes the structure of the response:
-include::{snippets}metrics/metric/response-fields.adoc[]
+include::{snippets}/metrics/metric/response-fields.adoc[]
[[metrics-drilling-down]]
@@ -68,9 +68,9 @@ include::{snippets}metrics/metric/response-fields.adoc[]
To drill down into a metric, make a `GET` request to `/actuator/metrics/{metric.name}` using the `tag` query parameter, as shown in the following curl-based example:
-include::{snippets}metrics/metric-with-tags/curl-request.adoc[]
+include::{snippets}/metrics/metric-with-tags/curl-request.adoc[]
The preceding example retrieves the `jvm.memory.max` metric, where the `area` tag has a value of `nonheap` and the `id` attribute has a value of `Compressed Class Space`.
The resulting response is similar to the following:
-include::{snippets}metrics/metric-with-tags/http-response.adoc[]
+include::{snippets}/metrics/metric-with-tags/http-response.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/prometheus.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/prometheus.adoc
similarity index 79%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/prometheus.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/prometheus.adoc
index 1d1006bc376..16d2205ba4a 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/prometheus.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/prometheus.adoc
@@ -10,8 +10,8 @@ The `prometheus` endpoint provides Spring Boot application's metrics in the form
To retrieve the metrics, make a `GET` request to `/actuator/prometheus`, as shown in the following curl-based example:
-include::{snippets}prometheus/curl-request.adoc[]
+include::{snippets}/prometheus/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}prometheus/http-response.adoc[]
+include::{snippets}/prometheus/http-response.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/scheduledtasks.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/scheduledtasks.adoc
similarity index 78%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/scheduledtasks.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/scheduledtasks.adoc
index a3a4f3a9a4c..1e69e6046a3 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/scheduledtasks.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/scheduledtasks.adoc
@@ -10,11 +10,11 @@ The `scheduledtasks` endpoint provides information about the application's sched
To retrieve the scheduled tasks, make a `GET` request to `/actuator/scheduledtasks`, as shown in the following curl-based example:
-include::{snippets}scheduled-tasks/curl-request.adoc[]
+include::{snippets}/scheduled-tasks/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}scheduled-tasks/http-response.adoc[]
+include::{snippets}/scheduled-tasks/http-response.adoc[]
@@ -25,4 +25,4 @@ The response contains details of the application's scheduled tasks.
The following table describes the structure of the response:
[cols="2,1,3"]
-include::{snippets}scheduled-tasks/response-fields.adoc[]
+include::{snippets}/scheduled-tasks/response-fields.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/sessions.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/sessions.adoc
similarity index 78%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/sessions.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/sessions.adoc
index 8cc3508d9e9..01f2d8a8428 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/sessions.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/sessions.adoc
@@ -10,12 +10,12 @@ The `sessions` endpoint provides information about the application's HTTP sessio
To retrieve the sessions, make a `GET` request to `/actuator/sessions`, as shown in the following curl-based example:
-include::{snippets}sessions/username/curl-request.adoc[]
+include::{snippets}/sessions/username/curl-request.adoc[]
The preceding examples retrieves all of the sessions for the user whose username is `alice`.
The resulting response is similar to the following:
-include::{snippets}sessions/username/http-response.adoc[]
+include::{snippets}/sessions/username/http-response.adoc[]
@@ -26,7 +26,7 @@ The endpoint uses query parameters to limit the sessions that it returns.
The following table shows the single required query parameter:
[cols="2,4"]
-include::{snippets}sessions/username/request-parameters.adoc[]
+include::{snippets}/sessions/username/request-parameters.adoc[]
@@ -37,7 +37,7 @@ The response contains details of the matching sessions.
The following table describes the structure of the response:
[cols="3,1,3"]
-include::{snippets}sessions/username/response-fields.adoc[]
+include::{snippets}/sessions/username/response-fields.adoc[]
@@ -46,12 +46,12 @@ include::{snippets}sessions/username/response-fields.adoc[]
To retrieve a single session, make a `GET` request to `/actuator/sessions/\{id}`, as shown in the following curl-based example:
-include::{snippets}sessions/id/curl-request.adoc[]
+include::{snippets}/sessions/id/curl-request.adoc[]
The preceding example retrieves the session with the `id` of `4db5efcc-99cb-4d05-a52c-b49acfbb7ea9`.
The resulting response is similar to the following:
-include::{snippets}sessions/id/http-response.adoc[]
+include::{snippets}/sessions/id/http-response.adoc[]
@@ -62,7 +62,7 @@ The response contains details of the requested session.
The following table describes the structure of the response:
[cols="3,1,3"]
-include::{snippets}sessions/id/response-fields.adoc[]
+include::{snippets}/sessions/id/response-fields.adoc[]
@@ -71,6 +71,6 @@ include::{snippets}sessions/id/response-fields.adoc[]
To delete a session, make a `DELETE` request to `/actuator/sessions/\{id}`, as shown in the following curl-based example:
-include::{snippets}sessions/delete/curl-request.adoc[]
+include::{snippets}/sessions/delete/curl-request.adoc[]
The preceding example deletes the session with the `id` of `4db5efcc-99cb-4d05-a52c-b49acfbb7ea9`.
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/shutdown.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/shutdown.adoc
similarity index 78%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/shutdown.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/shutdown.adoc
index 00aec5b1bf0..2231904f675 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/shutdown.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/shutdown.adoc
@@ -10,11 +10,11 @@ The `shutdown` endpoint is used to shut down the application.
To shut down the application, make a `POST` request to `/actuator/shutdown`, as shown in the following curl-based example:
-include::{snippets}shutdown/curl-request.adoc[]
+include::{snippets}/shutdown/curl-request.adoc[]
A response similar to the following is produced:
-include::{snippets}shutdown/http-response.adoc[]
+include::{snippets}/shutdown/http-response.adoc[]
@@ -25,4 +25,4 @@ The response contains details of the result of the shutdown request.
The following table describes the structure of the response:
[cols="3,1,3"]
-include::{snippets}shutdown/response-fields.adoc[]
+include::{snippets}/shutdown/response-fields.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/threaddump.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/threaddump.adoc
similarity index 76%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/threaddump.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/threaddump.adoc
index fa61f6df0c9..e141296d703 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/threaddump.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/threaddump.adoc
@@ -10,11 +10,11 @@ The `threaddump` endpoint provides a thread dump from the application's JVM.
To retrieve the thread dump as JSON, make a `GET` request to `/actuator/threaddump` with an appropriate `Accept` header, as shown in the following curl-based example:
-include::{snippets}threaddump/json/curl-request.adoc[]
+include::{snippets}/threaddump/json/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}threaddump/json/http-response.adoc[]
+include::{snippets}/threaddump/json/http-response.adoc[]
@@ -25,7 +25,7 @@ The response contains details of the JVM's threads.
The following table describes the structure of the response:
[cols="3,1,2"]
-include::{snippets}threaddump/json/response-fields.adoc[]
+include::{snippets}/threaddump/json/response-fields.adoc[]
@@ -35,8 +35,8 @@ include::{snippets}threaddump/json/response-fields.adoc[]
To retrieve the thread dump as text, make a `GET` request to `/actuator/threaddump` that
accepts `text/plain`, as shown in the following curl-based example:
-include::{snippets}threaddump/text/curl-request.adoc[]
+include::{snippets}/threaddump/text/curl-request.adoc[]
The resulting response is similar to the following:
-include::{snippets}threaddump/text/http-response.adoc[]
+include::{snippets}/threaddump/text/http-response.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/index.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/index.adoc
similarity index 100%
rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/index.adoc
rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/index.adoc
diff --git a/spring-boot-project/spring-boot-actuator/build.gradle b/spring-boot-project/spring-boot-actuator/build.gradle
new file mode 100644
index 00000000000..73dddb18803
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/build.gradle
@@ -0,0 +1,96 @@
+plugins {
+ id 'java-library'
+ id 'org.springframework.boot.conventions'
+ id 'org.springframework.boot.configuration-properties'
+ id 'org.springframework.boot.optional-dependencies'
+ id 'org.springframework.boot.deployed'
+}
+
+description = 'Spring Boot Actuator'
+
+dependencies {
+ implementation enforcedPlatform(project(':spring-boot-project:spring-boot-dependencies'))
+ implementation project(':spring-boot-project:spring-boot')
+
+ optional enforcedPlatform(project(':spring-boot-project:spring-boot-dependencies'))
+ optional 'com.fasterxml.jackson.core:jackson-databind'
+ optional 'com.hazelcast:hazelcast'
+ optional 'com.hazelcast:hazelcast-spring'
+ optional 'com.sun.mail:jakarta.mail'
+ optional 'com.zaxxer:HikariCP'
+ optional 'io.lettuce:lettuce-core'
+ optional 'io.micrometer:micrometer-core'
+ optional 'io.micrometer:micrometer-registry-prometheus'
+ optional 'io.prometheus:simpleclient_pushgateway'
+ optional 'io.reactivex:rxjava-reactive-streams'
+ optional 'io.searchbox:jest'
+ optional 'org.elasticsearch.client:elasticsearch-rest-client'
+ optional ('io.undertow:undertow-servlet') {
+ exclude group: 'org.jboss.spec.javax.annotation', module: 'jboss-annotations-api_1.2_spec'
+ exclude group: 'org.jboss.spec.javax.servlet', module: 'jboss-servlet-api_4.0_spec'
+ }
+ optional 'javax.cache:cache-api'
+ optional 'javax.jms:javax.jms-api'
+ optional 'net.sf.ehcache:ehcache'
+ optional 'org.apache.tomcat.embed:tomcat-embed-core'
+ optional 'org.aspectj:aspectjweaver'
+ optional 'org.eclipse.jetty:jetty-server'
+ optional 'org.elasticsearch:elasticsearch'
+ optional 'org.flywaydb:flyway-core'
+ optional 'org.glassfish.jersey.core:jersey-server'
+ optional 'org.glassfish.jersey.containers:jersey-container-servlet-core'
+ optional 'org.hibernate.validator:hibernate-validator'
+ optional 'org.infinispan:infinispan-spring4-embedded'
+ optional 'org.influxdb:influxdb-java'
+ optional 'org.liquibase:liquibase-core'
+ optional 'org.mongodb:mongodb-driver-async'
+ optional 'org.mongodb:mongodb-driver-reactivestreams'
+ optional 'org.springframework:spring-jdbc'
+ optional 'org.springframework:spring-messaging'
+ optional 'org.springframework:spring-webflux'
+ optional 'org.springframework:spring-web'
+ optional 'org.springframework:spring-webmvc'
+ optional 'org.springframework.amqp:spring-rabbit'
+ optional 'org.springframework.data:spring-data-cassandra'
+ optional 'org.springframework.data:spring-data-couchbase'
+ optional 'org.springframework.data:spring-data-ldap'
+ optional 'org.springframework.data:spring-data-mongodb'
+ optional 'org.springframework.data:spring-data-neo4j'
+ optional 'org.springframework.data:spring-data-redis'
+ optional 'org.springframework.data:spring-data-rest-webmvc'
+ optional 'org.springframework.data:spring-data-solr'
+ optional 'org.springframework.integration:spring-integration-core'
+ optional 'org.springframework.security:spring-security-core'
+ optional 'org.springframework.security:spring-security-web'
+ optional 'org.springframework.session:spring-session-core'
+
+ testImplementation project(':spring-boot-project:spring-boot-test')
+ testImplementation project(':spring-boot-project:spring-boot-tools:spring-boot-test-support')
+ testImplementation project(':spring-boot-project:spring-boot-autoconfigure')
+ testImplementation 'org.assertj:assertj-core'
+ testImplementation 'com.jayway.jsonpath:json-path'
+ testImplementation 'io.projectreactor:reactor-test'
+ testImplementation 'org.apache.logging.log4j:log4j-to-slf4j'
+ testImplementation 'org.awaitility:awaitility'
+ testImplementation 'org.glassfish.jersey.media:jersey-media-json-jackson'
+ testImplementation 'org.hamcrest:hamcrest'
+ testImplementation 'org.junit.jupiter:junit-jupiter'
+ testImplementation 'org.mockito:mockito-core'
+ testImplementation 'org.mockito:mockito-junit-jupiter'
+ testImplementation 'org.skyscreamer:jsonassert'
+ testImplementation 'org.springframework:spring-test'
+
+ testRuntimeOnly 'io.projectreactor.netty:reactor-netty'
+ testRuntimeOnly 'javax.xml.bind:jaxb-api'
+ testRuntimeOnly 'org.apache.tomcat.embed:tomcat-embed-el'
+ testRuntimeOnly 'org.glassfish.jersey.ext:jersey-spring4'
+ testRuntimeOnly 'org.hsqldb:hsqldb'
+}
+
+compileJava {
+ options.compilerArgs << '-parameters'
+}
+
+compileTestJava {
+ options.compilerArgs << '-parameters'
+}
\ No newline at end of file
diff --git a/spring-boot-project/spring-boot-actuator/pom.xml b/spring-boot-project/spring-boot-actuator/pom.xml
deleted file mode 100644
index cedc88716cf..00000000000
--- a/spring-boot-project/spring-boot-actuator/pom.xml
+++ /dev/null
@@ -1,401 +0,0 @@
-
-
- 4.0.0
-
- org.springframework.boot
- spring-boot-parent
- ${revision}
- ../spring-boot-parent
-
- spring-boot-actuator
- Spring Boot Actuator
- Spring Boot Actuator
-
- ${basedir}/../..
-
-
- ${git.url}
- ${git.connection}
- ${git.developerConnection}
-
-
-
-
- org.springframework.boot
- spring-boot
-
-
- com.fasterxml.jackson.datatype
- jackson-datatype-jsr310
-
-
-
- com.fasterxml.jackson.core
- jackson-databind
- true
-
-
- com.hazelcast
- hazelcast
- true
-
-
- com.hazelcast
- hazelcast-spring
- true
-
-
- com.sun.mail
- jakarta.mail
- true
-
-
- com.zaxxer
- HikariCP
- true
-
-
- io.lettuce
- lettuce-core
- true
-
-
- io.micrometer
- micrometer-core
- true
-
-
- io.micrometer
- micrometer-registry-prometheus
- true
-
-
- io.prometheus
- simpleclient_pushgateway
- true
-
-
- io.reactivex
- rxjava-reactive-streams
- true
-
-
- io.searchbox
- jest
- true
-
-
- org.elasticsearch.client
- elasticsearch-rest-client
- true
-
-
- io.undertow
- undertow-servlet
- true
-
-
- org.jboss.spec.javax.servlet
- jboss-servlet-api_3.1_spec
-
-
-
-
- javax.cache
- cache-api
- true
-
-
- jakarta.jms
- jakarta.jms-api
- true
-
-
- jakarta.ws.rs
- jakarta.ws.rs-api
- true
-
-
- net.sf.ehcache
- ehcache
- true
-
-
- org.apache.tomcat.embed
- tomcat-embed-core
- true
-
-
- org.aspectj
- aspectjweaver
- true
-
-
- org.eclipse.jetty
- jetty-server
- true
-
-
- javax.servlet
- javax.servlet-api
-
-
-
-
- org.elasticsearch
- elasticsearch
- true
-
-
- org.flywaydb
- flyway-core
- true
-
-
- org.glassfish.jersey.core
- jersey-server
- true
-
-
- javax.validation
- validation-api
-
-
-
-
- org.glassfish.jersey.containers
- jersey-container-servlet-core
- true
-
-
- org.hibernate.validator
- hibernate-validator
- true
-
-
- javax.validation
- validation-api
-
-
-
-
- org.infinispan
- infinispan-spring5-embedded
- true
-
-
- org.influxdb
- influxdb-java
- true
-
-
- org.liquibase
- liquibase-core
- true
-
-
- org.mongodb
- mongodb-driver-async
- true
-
-
- org.mongodb
- mongodb-driver-reactivestreams
- true
-
-
- org.springframework
- spring-jdbc
- true
-
-
- org.springframework
- spring-messaging
- true
-
-
- org.springframework
- spring-webflux
- true
-
-
- org.springframework
- spring-web
- true
-
-
- org.springframework
- spring-webmvc
- true
-
-
- org.springframework.amqp
- spring-rabbit
- true
-
-
- org.springframework.data
- spring-data-cassandra
- true
-
-
- org.springframework.data
- spring-data-couchbase
- true
-
-
- org.springframework.data
- spring-data-ldap
- true
-
-
- org.springframework.data
- spring-data-mongodb
- true
-
-
- org.springframework.data
- spring-data-neo4j
- true
-
-
- org.springframework.data
- spring-data-redis
- true
-
-
- org.springframework.data
- spring-data-rest-webmvc
- true
-
-
- org.springframework.data
- spring-data-solr
- true
-
-
-
- wstx-asl
- org.codehaus.woodstox
-
-
-
-
- org.springframework.integration
- spring-integration-core
- true
-
-
- org.springframework.security
- spring-security-core
- true
-
-
- org.springframework.security
- spring-security-web
- true
-
-
- org.springframework.session
- spring-session-core
- true
-
-
-
- org.springframework.boot
- spring-boot-configuration-processor
- true
-
-
-
- org.springframework.boot
- spring-boot-test
- test
-
-
- org.springframework.boot
- spring-boot-test-support
- test
-
-
- org.springframework.boot
- spring-boot-autoconfigure
- test
-
-
- ch.qos.logback
- logback-classic
- test
-
-
- jakarta.validation
- jakarta.validation-api
- test
-
-
- jakarta.xml.bind
- jakarta.xml.bind-api
- test
-
-
- org.apache.logging.log4j
- log4j-to-slf4j
- test
-
-
- org.apache.tomcat.embed
- tomcat-embed-el
- test
-
-
- org.glassfish.jersey.media
- jersey-media-json-jackson
- test
-
-
- org.awaitility
- awaitility
- test
-
-
- com.jayway.jsonpath
- json-path
- test
-
-
- io.projectreactor
- reactor-test
- test
-
-
- io.projectreactor.netty
- reactor-netty
- test
-
-
- org.hsqldb
- hsqldb
- test
-
-
- org.glassfish.jersey.ext
- jersey-spring5
- test
-
-
- javax.validation
- validation-api
-
-
-
-
- org.skyscreamer
- jsonassert
- test
-
-
-
-
diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebEndpointTestInvocationContextProvider.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebEndpointTestInvocationContextProvider.java
index 41bf39c07fa..c1669f45457 100644
--- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebEndpointTestInvocationContextProvider.java
+++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/test/WebEndpointTestInvocationContextProvider.java
@@ -66,6 +66,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
+import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.util.ClassUtils;
@@ -192,7 +193,13 @@ class WebEndpointTestInvocationContextProvider implements TestTemplateInvocation
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(
"http://localhost:" + determinePort());
uriBuilderFactory.setEncodingMode(EncodingMode.NONE);
- return WebTestClient.bindToServer().uriBuilderFactory(uriBuilderFactory).responseTimeout(TIMEOUT).build();
+ return WebTestClient.bindToServer().uriBuilderFactory(uriBuilderFactory).responseTimeout(TIMEOUT)
+ .filter((request, next) -> {
+ if (HttpMethod.GET == request.method()) {
+ return next.exchange(request).retry(10);
+ }
+ return next.exchange(request);
+ }).build();
}
private int determinePort() {
diff --git a/spring-boot-project/spring-boot-autoconfigure/build.gradle b/spring-boot-project/spring-boot-autoconfigure/build.gradle
new file mode 100644
index 00000000000..ff509e74258
--- /dev/null
+++ b/spring-boot-project/spring-boot-autoconfigure/build.gradle
@@ -0,0 +1,185 @@
+plugins {
+ id 'java-library'
+ id 'org.jetbrains.kotlin.jvm'
+ id 'org.springframework.boot.auto-configuration'
+ id 'org.springframework.boot.conventions'
+ id 'org.springframework.boot.deployed'
+ id 'org.springframework.boot.optional-dependencies'
+}
+
+description = 'Spring Boot AutoConfigure'
+
+dependencies {
+ api project(':spring-boot-project:spring-boot')
+
+ implementation enforcedPlatform(project(':spring-boot-project:spring-boot-dependencies'))
+
+ optional enforcedPlatform(project(':spring-boot-project:spring-boot-dependencies'))
+ optional 'com.atomikos:transactions-jdbc'
+ optional 'com.atomikos:transactions-jta'
+ optional 'com.couchbase.client:couchbase-spring-cache'
+ optional 'com.fasterxml.jackson.core:jackson-databind'
+ optional 'com.fasterxml.jackson.dataformat:jackson-dataformat-cbor'
+ optional 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml'
+ optional 'com.fasterxml.jackson.datatype:jackson-datatype-joda'
+ optional 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
+ optional 'com.fasterxml.jackson.module:jackson-module-parameter-names'
+ optional 'com.google.code.gson:gson'
+ optional 'com.hazelcast:hazelcast'
+ optional 'com.hazelcast:hazelcast-client'
+ optional 'com.hazelcast:hazelcast-spring'
+ optional 'com.h2database:h2'
+ optional 'com.samskivert:jmustache'
+ optional 'com.sun.mail:jakarta.mail'
+ optional 'de.flapdoodle.embed:de.flapdoodle.embed.mongo'
+ optional 'io.lettuce:lettuce-core'
+ optional 'io.projectreactor.netty:reactor-netty'
+ optional 'io.searchbox:jest'
+ optional 'io.rsocket:rsocket-core'
+ optional 'io.rsocket:rsocket-transport-netty'
+ optional ('io.undertow:undertow-servlet') {
+ exclude group: 'org.jboss.spec.javax.annotation', module: 'jboss-annotations-api_1.2_spec'
+ exclude group: 'org.jboss.spec.javax.servlet', module: 'jboss-servlet-api_4.0_spec'
+ }
+ optional ('io.undertow:undertow-websockets-jsr') {
+ exclude group: 'org.jboss.spec.javax.annotation', module: 'jboss-annotations-api_1.2_spec'
+ exclude group: 'org.jboss.spec.javax.servlet', module: 'jboss-servlet-api_4.0_spec'
+ }
+ optional 'jakarta.jms:jakarta.jms-api'
+ optional 'jakarta.mail:jakarta.mail-api'
+ optional 'jakarta.json.bind:jakarta.json.bind-api'
+ optional 'jakarta.persistence:jakarta.persistence-api'
+ optional 'jakarta.validation:jakarta.validation-api'
+ optional 'jakarta.ws.rs:jakarta.ws.rs-api'
+ optional 'javax.cache:cache-api'
+ optional 'javax.money:money-api'
+ optional 'net.sf.ehcache:ehcache'
+ optional 'org.apache.activemq:activemq-broker'
+ optional 'org.apache.activemq:artemis-jms-client'
+ optional 'org.apache.activemq:artemis-jms-server'
+ optional 'org.apache.commons:commons-dbcp2'
+ optional 'org.apache.kafka:kafka-streams'
+ optional 'org.apache.solr:solr-solrj'
+ optional 'org.apache.tomcat.embed:tomcat-embed-core'
+ optional 'org.apache.tomcat.embed:tomcat-embed-el'
+ optional 'org.apache.tomcat.embed:tomcat-embed-websocket'
+ optional 'org.apache.tomcat:tomcat-jdbc'
+ optional 'org.codehaus.btm:btm'
+ optional 'org.codehaus.groovy:groovy-templates'
+ optional 'com.github.ben-manes.caffeine:caffeine'
+ optional 'com.github.mxab.thymeleaf.extras:thymeleaf-extras-data-attribute'
+ optional 'com.sendgrid:sendgrid-java'
+ optional 'com.unboundid:unboundid-ldapsdk'
+ optional 'com.zaxxer:HikariCP'
+ optional 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
+ optional 'org.aspectj:aspectjweaver'
+ optional 'org.eclipse.jetty:jetty-webapp'
+ optional 'org.eclipse.jetty:jetty-reactive-httpclient'
+ optional 'org.eclipse.jetty.websocket:javax-websocket-server-impl'
+ optional 'org.ehcache:ehcache'
+ optional 'org.elasticsearch.client:elasticsearch-rest-client'
+ optional 'org.elasticsearch.client:elasticsearch-rest-high-level-client'
+ optional 'org.flywaydb:flyway-core'
+ optional 'org.freemarker:freemarker'
+ optional 'org.glassfish.jersey.core:jersey-server'
+ optional 'org.glassfish.jersey.containers:jersey-container-servlet-core'
+ optional 'org.glassfish.jersey.containers:jersey-container-servlet'
+ optional 'org.glassfish.jersey.ext:jersey-spring5'
+ optional 'org.glassfish.jersey.media:jersey-media-json-jackson'
+ optional 'org.hibernate:hibernate-core'
+ optional 'org.hibernate:hibernate-jcache'
+ optional 'org.hibernate.validator:hibernate-validator'
+ optional 'org.infinispan:infinispan-jcache'
+ optional 'org.infinispan:infinispan-spring5-embedded'
+ optional 'org.influxdb:influxdb-java'
+ optional 'org.jboss:jboss-transaction-spi'
+ optional 'org.jooq:jooq'
+ optional 'org.liquibase:liquibase-core'
+ optional 'org.messaginghub:pooled-jms'
+ optional 'org.mongodb:mongodb-driver-async'
+ optional 'org.mongodb:mongodb-driver-reactivestreams'
+ optional 'org.quartz-scheduler:quartz'
+ optional 'org.springframework:spring-jdbc'
+ optional 'org.springframework.integration:spring-integration-core'
+ optional 'org.springframework.integration:spring-integration-jdbc'
+ optional 'org.springframework.integration:spring-integration-jmx'
+ optional 'org.springframework:spring-jms'
+ optional 'org.springframework:spring-orm'
+ optional 'org.springframework:spring-tx'
+ optional 'org.springframework:spring-web'
+ optional 'org.springframework:spring-websocket'
+ optional 'org.springframework:spring-webflux'
+ optional 'org.springframework:spring-webmvc'
+ optional 'org.springframework.batch:spring-batch-core'
+ optional 'org.springframework.data:spring-data-couchbase'
+ optional 'org.springframework.data:spring-data-jpa'
+ optional 'org.springframework.data:spring-data-rest-webmvc'
+ optional 'org.springframework.data:spring-data-cassandra'
+ optional 'org.springframework.data:spring-data-elasticsearch'
+ optional 'org.springframework.data:spring-data-jdbc'
+ optional 'org.springframework.data:spring-data-ldap'
+ optional 'org.springframework.data:spring-data-mongodb'
+ optional 'org.springframework.data:spring-data-neo4j'
+ optional 'org.springframework.data:spring-data-redis'
+ optional 'org.springframework.data:spring-data-solr'
+ optional 'org.springframework.hateoas:spring-hateoas'
+ optional 'org.springframework.security:spring-security-acl'
+ optional 'org.springframework.security:spring-security-config'
+ optional 'org.springframework.security:spring-security-data'
+ optional 'org.springframework.security:spring-security-oauth2-client'
+ optional 'org.springframework.security:spring-security-oauth2-jose'
+ optional 'org.springframework.security:spring-security-oauth2-resource-server'
+ optional 'org.springframework.security:spring-security-rsocket'
+ optional 'org.springframework.security:spring-security-saml2-service-provider'
+ optional 'org.springframework.security:spring-security-web'
+ optional 'org.springframework.session:spring-session-core'
+ optional 'org.springframework.session:spring-session-data-mongodb'
+ optional 'org.springframework.session:spring-session-data-redis'
+ optional 'org.springframework.session:spring-session-hazelcast'
+ optional 'org.springframework.session:spring-session-jdbc'
+ optional 'org.springframework.amqp:spring-rabbit'
+ optional 'org.springframework.kafka:spring-kafka'
+ optional 'org.springframework.cloud:spring-cloud-spring-service-connector'
+ optional 'org.springframework.ws:spring-ws-core'
+ optional 'org.thymeleaf:thymeleaf'
+ optional 'org.thymeleaf:thymeleaf-spring5'
+ optional 'org.thymeleaf.extras:thymeleaf-extras-java8time'
+ optional 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
+ optional 'redis.clients:jedis'
+
+ testImplementation enforcedPlatform(project(':spring-boot-project:spring-boot-parent'))
+ testImplementation project(':spring-boot-project:spring-boot-tools:spring-boot-test-support')
+ testImplementation project(':spring-boot-project:spring-boot-test')
+ testImplementation 'ch.qos.logback:logback-classic'
+ testImplementation 'commons-fileupload:commons-fileupload'
+ testImplementation 'com.atomikos:transactions-jms'
+ testImplementation 'com.jayway.jsonpath:json-path'
+ testImplementation 'com.squareup.okhttp3:mockwebserver'
+ testImplementation 'com.sun.xml.messaging.saaj:saaj-impl'
+ testImplementation 'jakarta.json:jakarta.json-api'
+ testImplementation 'jakarta.xml.ws:jakarta.xml.ws-api'
+ testImplementation 'mysql:mysql-connector-java'
+ testImplementation 'org.apache.johnzon:johnzon-jsonb'
+ testImplementation 'org.apache.logging.log4j:log4j-to-slf4j'
+ testImplementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
+ testImplementation 'org.assertj:assertj-core'
+ testImplementation 'org.awaitility:awaitility'
+ testImplementation 'org.hsqldb:hsqldb'
+ testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
+ testImplementation 'org.junit.jupiter:junit-jupiter'
+ testImplementation 'org.neo4j:neo4j-ogm-bolt-native-types'
+ testImplementation 'org.neo4j:neo4j-ogm-http-driver'
+ testImplementation 'org.neo4j:neo4j-ogm-embedded-driver'
+ testImplementation 'org.springframework:spring-test'
+ testImplementation 'org.springframework.kafka:spring-kafka-test'
+ testImplementation 'org.springframework.security:spring-security-test'
+ testImplementation 'org.testcontainers:cassandra'
+ testImplementation 'org.testcontainers:couchbase'
+ testImplementation 'org.testcontainers:elasticsearch'
+ testImplementation 'org.testcontainers:junit-jupiter'
+ testImplementation 'org.testcontainers:testcontainers'
+ testImplementation 'org.yaml:snakeyaml'
+
+ testRuntimeOnly 'org.jetbrains.kotlin:kotlin-reflect'
+ testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
+}
diff --git a/spring-boot-project/spring-boot-autoconfigure/pom.xml b/spring-boot-project/spring-boot-autoconfigure/pom.xml
deleted file mode 100755
index a187161806f..00000000000
--- a/spring-boot-project/spring-boot-autoconfigure/pom.xml
+++ /dev/null
@@ -1,1044 +0,0 @@
-
-
- 4.0.0
-
- org.springframework.boot
- spring-boot-parent
- ${revision}
- ../spring-boot-parent
-
- spring-boot-autoconfigure
- Spring Boot AutoConfigure
- Spring Boot AutoConfigure
-
- ${basedir}/../..
-
-
- ${git.url}
- ${git.connection}
- ${git.developerConnection}
-
-
-
-
- org.springframework.boot
- spring-boot
-
-
-
- com.atomikos
- transactions-jdbc
- true
-
-
- com.atomikos
- transactions-jta
- true
-
-
- com.couchbase.client
- couchbase-spring-cache
- true
-
-
- com.fasterxml.jackson.core
- jackson-databind
- true
-
-
- com.fasterxml.jackson.dataformat
- jackson-dataformat-cbor
- true
-
-
- com.fasterxml.jackson.dataformat
- jackson-dataformat-xml
- true
-
-
- com.fasterxml.jackson.datatype
- jackson-datatype-joda
- true
-
-
- com.fasterxml.jackson.datatype
- jackson-datatype-jsr310
- true
-
-
- com.fasterxml.jackson.module
- jackson-module-parameter-names
- true
-
-
- com.google.code.gson
- gson
- true
-
-
- com.hazelcast
- hazelcast
- true
-
-
- com.hazelcast
- hazelcast-client
- true
-
-
- com.hazelcast
- hazelcast-spring
- true
-
-
- com.h2database
- h2
- true
-
-
- com.samskivert
- jmustache
- true
-
-
- com.sun.mail
- jakarta.mail
- true
-
-
- de.flapdoodle.embed
- de.flapdoodle.embed.mongo
- true
-
-
- io.lettuce
- lettuce-core
- true
-
-
- io.projectreactor.netty
- reactor-netty
- true
-
-
- io.rsocket
- rsocket-core
- true
-
-
- io.rsocket
- rsocket-transport-netty
- true
-
-
- io.searchbox
- jest
- true
-
-
- jakarta.json.bind
- jakarta.json.bind-api
- true
-
-
- javax.cache
- cache-api
- true
-
-
- javax.money
- money-api
- true
-
-
- org.apache.kafka
- kafka-streams
- true
-
-
- javax.ws.rs
- javax.ws.rs-api
-
-
-
-
- org.flywaydb
- flyway-core
- true
-
-
- org.glassfish.jersey.core
- jersey-server
- true
-
-
- javax.validation
- validation-api
-
-
-
-
- org.glassfish.jersey.containers
- jersey-container-servlet-core
- true
-
-
- org.glassfish.jersey.containers
- jersey-container-servlet
- true
-
-
- org.glassfish.jersey.ext
- jersey-spring5
- true
-
-
- org.glassfish.hk2.external
- bean-validator
-
-
- org.hibernate
- hibernate-validator
-
-
-
-
- org.glassfish.jersey.media
- jersey-media-json-jackson
- true
-
-
- org.apache.activemq
- activemq-broker
- true
-
-
- geronimo-jms_1.1_spec
- org.apache.geronimo.specs
-
-
-
-
- org.apache.activemq
- artemis-jms-client
- true
-
-
- geronimo-jms_2.0_spec
- org.apache.geronimo.specs
-
-
-
-
- org.apache.activemq
- artemis-jms-server
- true
-
-
- geronimo-jms_2.0_spec
- org.apache.geronimo.specs
-
-
-
-
- org.apache.commons
- commons-dbcp2
- true
-
-
- org.apache.solr
- solr-solrj
- true
-
-
- org.codehaus.woodstox
- wstx-asl
-
-
- log4j
- log4j
-
-
-
-
- org.apache.tomcat.embed
- tomcat-embed-core
- true
-
-
- org.apache.tomcat.embed
- tomcat-embed-el
- true
-
-
- org.apache.tomcat.embed
- tomcat-embed-websocket
- true
-
-
- org.apache.tomcat
- tomcat-jdbc
- true
-
-
- org.codehaus.btm
- btm
- true
-
-
- javax.transaction
- jta
-
-
-
-
- org.codehaus.groovy
- groovy-templates
- true
-
-
- com.sendgrid
- sendgrid-java
- true
-
-
- com.unboundid
- unboundid-ldapsdk
- true
-
-
- com.zaxxer
- HikariCP
- true
-
-
- org.eclipse.jetty
- jetty-webapp
- true
-
-
- javax.servlet
- javax.servlet-api
-
-
-
-
- org.eclipse.jetty
- jetty-reactive-httpclient
- true
-
-
- org.eclipse.jetty.websocket
- javax-websocket-server-impl
- true
-
-
- javax.annotation
- javax.annotation-api
-
-
- javax.servlet
- javax.servlet-api
-
-
- javax.websocket
- javax.websocket-api
-
-
- javax.websocket
- javax.websocket-client-api
-
-
-
-
- io.undertow
- undertow-servlet
- true
-
-
- io.undertow
- undertow-websockets-jsr
- true
-
-
- jakarta.persistence
- jakarta.persistence-api
- true
-
-
- jakarta.validation
- jakarta.validation-api
- true
-
-
- jakarta.ws.rs
- jakarta.ws.rs-api
- true
-
-
- org.ehcache
- ehcache
- true
-
-
- org.elasticsearch.client
- elasticsearch-rest-client
- true
-
-
- org.elasticsearch.client
- elasticsearch-rest-high-level-client
- true
-
-
- org.freemarker
- freemarker
- true
-
-
- org.hibernate
- hibernate-core
- true
-
-
- javax.activation
- javax.activation-api
-
-
- javax.persistence
- javax.persistence-api
-
-
- javax.xml.bind
- jaxb-api
-
-
-
-
- org.hibernate
- hibernate-jcache
- true
-
-
- org.hibernate.validator
- hibernate-validator
- true
-
-
- javax.validation
- validation-api
-
-
-
-
- org.infinispan
- infinispan-jcache
- true
-
-
- org.infinispan
- infinispan-spring5-embedded
- true
-
-
- org.jboss
- jboss-transaction-spi
- true
-
-
- org.messaginghub
- pooled-jms
- true
-
-
- org.apache.geronimo.specs
- geronimo-jms_2.0_spec
-
-
-
-
- org.mongodb
- mongodb-driver-async
- true
-
-
- org.mongodb
- mongodb-driver-reactivestreams
- true
-
-
- org.springframework
- spring-jdbc
- true
-
-
- org.springframework.integration
- spring-integration-core
- true
-
-
- org.springframework.integration
- spring-integration-jdbc
- true
-
-
- org.springframework.integration
- spring-integration-jmx
- true
-
-
- org.springframework
- spring-jms
- true
-
-
- org.springframework
- spring-orm
- true
-
-
- org.springframework
- spring-tx
- true
-
-
- org.springframework
- spring-web
- true
-
-
- org.springframework
- spring-websocket
- true
-
-
- org.springframework
- spring-webflux
- true
-
-
- org.springframework
- spring-webmvc
- true
-
-
- org.springframework.batch
- spring-batch-core
- true
-
-
- org.springframework.data
- spring-data-couchbase
- true
-
-
- org.slf4j
- jcl-over-slf4j
-
-
-
-
- org.springframework.data
- spring-data-jpa
- true
-
-
- jcl-over-slf4j
- org.slf4j
-
-
-
-
- org.springframework.data
- spring-data-rest-webmvc
- true
-
-
- jcl-over-slf4j
- org.slf4j
-
-
-
-
- org.springframework.data
- spring-data-cassandra
- true
-
-
- org.springframework.data
- spring-data-jdbc
- true
-
-
- org.springframework.data
- spring-data-ldap
- true
-
-
- org.springframework.data
- spring-data-mongodb
- true
-
-
- jcl-over-slf4j
- org.slf4j
-
-
-
-
- org.springframework.data
- spring-data-neo4j
- true
-
-
- jcl-over-slf4j
- org.slf4j
-
-
-
-
- org.springframework.data
- spring-data-redis
- true
-
-
- org.springframework.data
- spring-data-elasticsearch
- true
-
-
- jcl-over-slf4j
- org.slf4j
-
-
-
-
- org.springframework.data
- spring-data-solr
- true
-
-
- jcl-over-slf4j
- org.slf4j
-
-
-
-
- org.springframework.hateoas
- spring-hateoas
- true
-
-
- redis.clients
- jedis
- true
-
-
- org.liquibase
- liquibase-core
- true
-
-
- org.springframework.security
- spring-security-acl
- true
-
-
- org.springframework.security
- spring-security-config
- true
-
-
- org.springframework.security
- spring-security-data
- true
-
-
- javax.xml.bind
- jaxb-api
-
-
-
-
- org.springframework.security
- spring-security-oauth2-client
- true
-
-
- com.sun.mail
- javax.mail
-
-
-
-
- org.springframework.security
- spring-security-oauth2-jose
- true
-
-
- org.springframework.security
- spring-security-oauth2-resource-server
- true
-
-
- org.springframework.security
- spring-security-rsocket
- true
-
-
- org.springframework.security
- spring-security-saml2-service-provider
- true
-
-
- org.springframework.security
- spring-security-web
- true
-
-
- org.springframework.session
- spring-session-core
- true
-
-
- org.springframework.session
- spring-session-data-mongodb
- true
-
-
- org.springframework.session
- spring-session-data-redis
- true
-
-
- org.springframework.session
- spring-session-hazelcast
- true
-
-
- javax.annotation
- javax.annotation-api
-
-
-
-
- org.springframework.session
- spring-session-jdbc
- true
-
-
- org.springframework.amqp
- spring-rabbit
- true
-
-
- org.springframework.kafka
- spring-kafka
- true
-
-
- org.springframework.cloud
- spring-cloud-spring-service-connector
- true
-
-
- org.springframework.ws
- spring-ws-core
- true
-
-
- org.thymeleaf
- thymeleaf
- true
-
-
- org.thymeleaf
- thymeleaf-spring5
- true
-
-
- nz.net.ultraq.thymeleaf
- thymeleaf-layout-dialect
- true
-
-
- com.github.ben-manes.caffeine
- caffeine
- true
-
-
- com.github.mxab.thymeleaf.extras
- thymeleaf-extras-data-attribute
- true
-
-
- org.thymeleaf.extras
- thymeleaf-extras-java8time
- true
-
-
- org.thymeleaf.extras
- thymeleaf-extras-springsecurity5
- true
-
-
- jakarta.jms
- jakarta.jms-api
- true
-
-
- jakarta.mail
- jakarta.mail-api
- true
-
-
- net.sf.ehcache
- ehcache
- true
-
-
- org.aspectj
- aspectjweaver
- true
-
-
- org.influxdb
- influxdb-java
- true
-
-
- org.jooq
- jooq
- true
-
-
- javax.xml.bind
- jaxb-api
-
-
-
-
- org.quartz-scheduler
- quartz
- true
-
-
-
- org.springframework.boot
- spring-boot-autoconfigure-processor
- true
-
-
- org.springframework.boot
- spring-boot-configuration-processor
- true
-
-
-
- org.jetbrains.kotlin
- kotlin-reflect
- test
-
-
- org.jetbrains.kotlin
- kotlin-stdlib-jdk8
- test
-
-
- org.springframework.boot
- spring-boot-test-support
- test
-
-
- org.springframework.boot
- spring-boot-test
- test
-
-
- ch.qos.logback
- logback-classic
- test
-
-
- commons-fileupload
- commons-fileupload
- test
-
-
- com.atomikos
- transactions-jms
- test
-
-
- com.jayway.jsonpath
- json-path
- test
-
-
- com.squareup.okhttp3
- mockwebserver
- test
-
-
- org.hamcrest
- hamcrest-core
-
-
-
-
- com.sun.xml.messaging.saaj
- saaj-impl
- test
-
-
- jakarta.json
- jakarta.json-api
- test
-
-
- jakarta.xml.ws
- jakarta.xml.ws-api
- test
-
-
- mysql
- mysql-connector-java
- test
-
-
- org.apache.johnzon
- johnzon-jsonb
- test
-
-
- org.apache.logging.log4j
- log4j-to-slf4j
- test
-
-
- org.apache.tomcat.embed
- tomcat-embed-jasper
- test
-
-
- org.awaitility
- awaitility
- test
-
-
- org.hsqldb
- hsqldb
- test
-
-
- org.neo4j
- neo4j-ogm-bolt-native-types
- test
-
-
- org.neo4j
- neo4j-ogm-embedded-driver
- test
-
-
- org.neo4j
- neo4j-ogm-http-driver
- test
-
-
- org.springframework
- spring-test
- test
-
-
- org.springframework.kafka
- spring-kafka-test
- test
-
-
- org.springframework.security
- spring-security-test
- test
-
-
- org.testcontainers
- cassandra
- test
-
-
- org.testcontainers
- couchbase
- test
-
-
- org.testcontainers
- elasticsearch
- test
-
-
- org.testcontainers
- junit-jupiter
- test
-
-
- org.testcontainers
- testcontainers
- test
-
-
- javax.annotation
- javax.annotation-api
-
-
- javax.xml.bind
- jaxb-api
-
-
-
-
- org.yaml
- snakeyaml
- test
-
-
-
-
-
- org.jetbrains.kotlin
- kotlin-maven-plugin
-
-
- test-compile
- test-compile
-
- test-compile
-
-
-
- ${project.basedir}/src/test/kotlin
- ${project.basedir}/src/test/java
-
-
-
-
-
-
-
-
-
- java9+
-
- [9,)
-
-
-
- org.glassfish.jaxb
- jaxb-runtime
- true
-
-
-
-
-
diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveRestClientAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveRestClientAutoConfigurationTests.java
index 8ccaf257f98..614c598ae7f 100644
--- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveRestClientAutoConfigurationTests.java
+++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/elasticsearch/ReactiveRestClientAutoConfigurationTests.java
@@ -74,10 +74,11 @@ public class ReactiveRestClientAutoConfigurationTests {
@Test
void restClientCanQueryElasticsearchNode() {
- this.contextRunner
- .withPropertyValues("spring.data.elasticsearch.client.reactive.endpoints="
- + elasticsearch.getContainerIpAddress() + ":" + elasticsearch.getFirstMappedPort())
- .run((context) -> {
+ this.contextRunner.withPropertyValues(
+ "spring.data.elasticsearch.client.reactive.endpoints=" + elasticsearch.getContainerIpAddress() + ":"
+ + elasticsearch.getFirstMappedPort(),
+ "spring.data.elasticsearch.client.reactive.connection-timeout=120s",
+ "spring.data.elasticsearch.client.reactive.socket-timeout=120s").run((context) -> {
ReactiveElasticsearchClient client = context.getBean(ReactiveElasticsearchClient.class);
Map source = new HashMap<>();
source.put("a", "alpha");
diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfigurationTests.java
index ba889f9762c..2461c1eaa95 100644
--- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfigurationTests.java
+++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfigurationTests.java
@@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.transaction;
+import java.util.UUID;
+
import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
@@ -81,7 +83,7 @@ class TransactionAutoConfigurationTests {
DataSourceTransactionManagerAutoConfiguration.class))
.withUserConfiguration(SinglePlatformTransactionManagerConfiguration.class,
SingleReactiveTransactionManagerConfiguration.class)
- .run((context) -> {
+ .withPropertyValues("spring.datasource.url:jdbc:h2:mem:" + UUID.randomUUID()).run((context) -> {
PlatformTransactionManager platformTransactionManager = context
.getBean(PlatformTransactionManager.class);
TransactionTemplate transactionTemplate = context.getBean(TransactionTemplate.class);
diff --git a/spring-boot-project/spring-boot-cli/build.gradle b/spring-boot-project/spring-boot-cli/build.gradle
new file mode 100644
index 00000000000..db7ee46c8d5
--- /dev/null
+++ b/spring-boot-project/spring-boot-cli/build.gradle
@@ -0,0 +1,195 @@
+plugins {
+ id 'java'
+ id 'org.springframework.boot.deployed'
+ id 'org.springframework.boot.conventions'
+ id 'org.springframework.boot.integration-test'
+}
+
+description = "Spring Boot CLI"
+
+configurations {
+ dependenciesBom
+ loader
+ testRepository
+}
+
+dependencies {
+ compileOnly project(':spring-boot-project:spring-boot')
+
+ compileOnly 'jakarta.servlet:jakarta.servlet-api'
+ compileOnly 'org.codehaus.groovy:groovy-templates'
+ compileOnly 'org.springframework:spring-web'
+
+ dependenciesBom project(path: ':spring-boot-project:spring-boot-dependencies', configuration: 'effectiveBom')
+
+ implementation enforcedPlatform(project(':spring-boot-project:spring-boot-parent'))
+ implementation project(':spring-boot-project:spring-boot-tools:spring-boot-loader-tools')
+
+ implementation 'com.vaadin.external.google:android-json'
+ implementation 'jline:jline'
+ implementation 'net.sf.jopt-simple:jopt-simple'
+ implementation('org.apache.httpcomponents:httpclient') {
+ exclude group: 'commons-logging', module: 'commons-logging'
+ }
+ implementation 'org.apache.maven:maven-model'
+ implementation('org.apache.maven:maven-resolver-provider') {
+ exclude group: 'com.google.guava', module: 'guava'
+ }
+ implementation 'org.apache.maven.resolver:maven-resolver-connector-basic'
+ implementation 'org.apache.maven.resolver:maven-resolver-transport-file'
+ implementation('org.apache.maven.resolver:maven-resolver-transport-http') {
+ exclude group: 'org.slf4j', module: 'jcl-over-slf4j'
+ }
+ implementation 'org.apache.maven:maven-settings-builder'
+ implementation 'org.codehaus.groovy:groovy'
+ implementation 'org.slf4j:slf4j-simple'
+ implementation 'org.sonatype.plexus:plexus-sec-dispatcher'
+ implementation('org.sonatype.sisu:sisu-inject-plexus') {
+ exclude group: 'javax.enterprise', module: 'cdi-api'
+ exclude group: 'org.sonatype.sisu', module: 'sisu-inject-bean'
+ }
+ implementation 'org.springframework:spring-core'
+ implementation 'org.springframework.security:spring-security-crypto'
+
+ intTestImplementation enforcedPlatform(project(':spring-boot-project:spring-boot-dependencies'))
+ intTestImplementation project(':spring-boot-project:spring-boot-tools:spring-boot-loader-tools')
+ intTestImplementation project(':spring-boot-project:spring-boot-tools:spring-boot-test-support')
+ intTestImplementation 'org.assertj:assertj-core'
+ intTestImplementation 'org.junit.jupiter:junit-jupiter'
+ intTestImplementation 'org.springframework:spring-core'
+
+ loader project(':spring-boot-project:spring-boot-tools:spring-boot-loader')
+
+ testImplementation project(':spring-boot-project:spring-boot')
+ testImplementation project(':spring-boot-project:spring-boot-tools:spring-boot-test-support')
+ testImplementation project(':spring-boot-project:spring-boot-test')
+ testImplementation 'org.assertj:assertj-core'
+ testImplementation 'org.codehaus.groovy:groovy-templates'
+ testImplementation 'org.junit.jupiter:junit-jupiter'
+ testImplementation 'org.mockito:mockito-core'
+ testImplementation 'org.springframework:spring-test'
+
+ testRepository project(path: ':spring-boot-project:spring-boot-starters:spring-boot-starter-actuator', configuration: 'mavenRepository')
+ testRepository project(path: ':spring-boot-project:spring-boot-starters:spring-boot-starter-amqp', configuration: 'mavenRepository')
+ testRepository project(path: ':spring-boot-project:spring-boot-starters:spring-boot-starter-aop', configuration: 'mavenRepository')
+ testRepository project(path: ':spring-boot-project:spring-boot-starters:spring-boot-starter-artemis', configuration: 'mavenRepository')
+ testRepository project(path: ':spring-boot-project:spring-boot-starters:spring-boot-starter-batch', configuration: 'mavenRepository')
+ testRepository project(path: ':spring-boot-project:spring-boot-starters:spring-boot-starter-data-jpa', configuration: 'mavenRepository')
+ testRepository project(path: ':spring-boot-project:spring-boot-starters:spring-boot-starter-jdbc', configuration: 'mavenRepository')
+ testRepository project(path: ':spring-boot-project:spring-boot-starters:spring-boot-starter-integration', configuration: 'mavenRepository')
+ testRepository project(path: ':spring-boot-project:spring-boot-starters:spring-boot-starter-security', configuration: 'mavenRepository')
+ testRepository project(path: ':spring-boot-project:spring-boot-starters:spring-boot-starter-web', configuration: 'mavenRepository')
+}
+
+task syncSpringBootDependenciesBom(type: Sync) {
+ destinationDir = file("$buildDir/generated-resources/org/springframework/boot/cli/compiler/dependencies")
+ from configurations.dependenciesBom
+}
+
+task syncTestRepository(type: Sync) {
+ destinationDir = file("${buildDir}/test-repository")
+ from configurations.testRepository
+}
+
+sourceSets {
+ main {
+ output.dir("$buildDir/generated-resources", builtBy: 'syncSpringBootDependenciesBom')
+ }
+}
+
+test {
+ dependsOn syncTestRepository
+ useJUnitPlatform()
+}
+
+task fullJar(type: Jar) {
+ classifier = 'full'
+ entryCompression = 'stored'
+ from(configurations.runtimeClasspath) {
+ into 'BOOT-INF/lib'
+ }
+ from(sourceSets.main.output) {
+ into 'BOOT-INF/classes'
+ }
+ into("") {
+ from zipTree(configurations.loader.singleFile)
+ }
+ manifest {
+ attributes(
+ 'Class-Loader': 'groovy.lang.GroovyClassLoader',
+ 'Main-Class': 'org.springframework.boot.loader.JarLauncher',
+ 'Start-Class': 'org.springframework.boot.cli.SpringCli'
+ )
+ }
+}
+
+def configureArchive(archive) {
+ archive.classifier = 'bin'
+ archive.into "spring-${project.version}"
+ archive.from(fullJar) {
+ rename {
+ it.replace("-full", "")
+ }
+ into 'lib/'
+ }
+ archive.from(file('src/main/content')) {
+ eachFile { it.mode = it.directory ? 0x755 : 0x644 }
+ }
+ archive.from(file('src/main/executablecontent')) {
+ eachFile { it.mode = 0x755 }
+ }
+}
+
+task zip(type: Zip) {
+ classifier = 'bin'
+ configureArchive it
+}
+
+intTest {
+ dependsOn syncTestRepository, zip
+}
+
+task tar(type: Tar) {
+ compression = 'gzip'
+ archiveExtension = 'tar.gz'
+ configureArchive it
+}
+
+task scoopManifest(type: org.springframework.boot.build.cli.ScoopManifest) {
+ dependsOn zip
+ outputDir = file("$buildDir/scoop")
+ template = file('src/main/scoop/springboot.json')
+ archive = zip.archiveFile
+}
+
+def scoopManifestArtifact = artifacts.add('archives', file("$buildDir/scoop/springboot.json")) {
+ type 'json'
+ classifier 'scoop'
+ builtBy 'scoopManifest'
+}
+
+task homebrewFormula(type: org.springframework.boot.build.cli.HomebrewFormula) {
+ dependsOn tar
+ outputDir = file("$buildDir/homebrew")
+ template = file('src/main/homebrew/springboot.rb')
+ archive = tar.archiveFile
+}
+
+def homebrewFormulaArtifact = artifacts.add('archives', file("$buildDir/homebrew/springboot.rb")) {
+ type 'rb'
+ classifier 'homebrew'
+ builtBy 'homebrewFormula'
+}
+
+publishing {
+ publications {
+ getByName('maven') {
+ artifact fullJar
+ artifact tar
+ artifact zip
+ artifact scoopManifestArtifact
+ artifact homebrewFormulaArtifact
+ }
+ }
+}
+
diff --git a/spring-boot-project/spring-boot-cli/pom.xml b/spring-boot-project/spring-boot-cli/pom.xml
deleted file mode 100644
index a349767d00a..00000000000
--- a/spring-boot-project/spring-boot-cli/pom.xml
+++ /dev/null
@@ -1,469 +0,0 @@
-
-
- 4.0.0
-
- org.springframework.boot
- spring-boot-parent
- ${revision}
- ../spring-boot-parent
-
- spring-boot-cli
- Spring Boot CLI
- Spring Boot CLI
-
- ${basedir}/../..
- org.springframework.boot.cli.SpringCli
- default
- ${project.build.directory}/generated-resources/org/springframework/boot/cli/compiler/dependencies
-
-
- ${git.url}
- ${git.connection}
- ${git.developerConnection}
-
-
-
-
- org.springframework.boot
- spring-boot-loader-tools
-
-
- com.vaadin.external.google
- android-json
-
-
- jline
- jline
-
-
- net.sf.jopt-simple
- jopt-simple
-
-
- org.codehaus.groovy
- groovy
-
-
- org.sonatype.plexus
- plexus-sec-dispatcher
-
-
- org.sonatype.sisu
- sisu-inject-plexus
-
-
- org.sonatype.sisu
- sisu-inject-bean
-
-
- javax.enterprise
- cdi-api
-
-
-
-
- org.springframework
- spring-core
-
-
- org.springframework.security
- spring-security-crypto
-
-
- org.apache.httpcomponents
- httpclient
-
-
- org.apache.maven
- maven-model
-
-
- org.apache.maven
- maven-settings-builder
-
-
- org.apache.maven
- maven-resolver-provider
-
-
- com.google.guava
- guava
-
-
-
-
- org.apache.maven.resolver
- maven-resolver-connector-basic
-
-
- org.apache.maven.resolver
- maven-resolver-impl
-
-
- org.apache.maven.resolver
- maven-resolver-transport-file
-
-
- org.apache.maven.resolver
- maven-resolver-transport-http
-
-
- jcl-over-slf4j
- org.slf4j
-
-
-
-
-
- org.springframework.boot
- spring-boot-dependencies
- effective-pom
- provided
- ${project.version}
-
-
- org.codehaus.groovy
- groovy-templates
- provided
-
-
- org.springframework.boot
- spring-boot
- provided
-
-
- org.springframework
- spring-web
- provided
-
-
- jakarta.servlet
- jakarta.servlet-api
- provided
-
-
-
- org.springframework.boot
- spring-boot-test-support
- test
-
-
- org.springframework.boot
- spring-boot-test
- test
-
-
- junit
- junit
- test
-
-
- org.hamcrest
- hamcrest-core
-
-
-
-
-
-
-
- ${project.build.directory}/generated-resources
-
-
- ${basedir}/src/main/resources
-
-
-
-
- org.apache.maven.plugins
- maven-failsafe-plugin
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
-
- ${project.build.directory}/generated-resources
-
-
- ${spring.profiles.active}
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
-
-
- copy-effective-pom
- generate-resources
-
- copy
-
-
-
-
- org.springframework.boot
- spring-boot-dependencies
- effective-pom
- true
- ${generated.pom.dir}
- effective-pom.xml
-
-
-
-
-
- unpack
- prepare-package
-
- unpack
-
-
-
-
- org.springframework.boot
- spring-boot-loader
- ${project.version}
- jar
-
-
- ${project.build.directory}/assembly
-
-
-
- copy
- prepare-package
-
- copy-dependencies
-
-
- ${project.build.directory}/assembly/BOOT-INF/lib
- runtime
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
- jar-with-dependencies
- package
-
- single
-
-
-
- src/main/assembly/jar-with-dependencies.xml
-
-
-
- true
- org.springframework.boot.loader.JarLauncher
-
-
- ${start-class}
- groovy.lang.GroovyClassLoader
-
-
-
-
-
- bin-package
- package
-
- single
-
-
-
- src/main/assembly/bin-package.xml
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-antrun-plugin
-
-
- ant-contrib
- ant-contrib
- 1.0b3
-
-
- ant
- ant
-
-
-
-
- org.apache.ant
- ant-nodeps
- 1.8.1
-
-
- org.tigris.antelope
- antelopetasks
- 3.2.10
-
-
-
-
- homebrew
- package
-
- run
-
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- scoop
- package
-
- run
-
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-