Compare commits

..

No commits in common. 'main' and '2.7.x' have entirely different histories.
main ... 2.7.x

@ -1,7 +1,5 @@
# .git-blame-ignore-revs # .git-blame-ignore-revs
# Reformat code following spring-javaformat upgrade # Reformat code following spring-javaformat upgrade
df5898a1464112f185d295d585740de696934a12 df5898a1464112f185d295d585740de696934a12
c4de86c244acdcff69ed0aecacd254399be79ce2
b07269a018a4a9d4c029aba7dd8a15fa66df681c

@ -10,10 +10,10 @@ jobs:
runs-on: ubuntu22-8-32 runs-on: ubuntu22-8-32
if: ${{ github.repository == 'spring-projects/spring-boot' }} if: ${{ github.repository == 'spring-projects/spring-boot' }}
steps: steps:
- name: Set up JDK 17 - name: Set up JDK 8
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
java-version: '17' java-version: '8'
distribution: 'liberica' distribution: 'liberica'
- name: Check out code - name: Check out code

@ -2,16 +2,6 @@
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="LombokGetterMayBeUsed" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="LombokGetterMayBeUsed" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="NullableProblems" enabled="false" level="WARNING" enabled_by_default="false">
<option name="REPORT_NULLABLE_METHOD_OVERRIDES_NOTNULL" value="true" />
<option name="REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL" value="true" />
<option name="REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE" value="true" />
<option name="REPORT_NOT_ANNOTATED_PARAMETER_OVERRIDES_NOTNULL" value="true" />
<option name="REPORT_NOT_ANNOTATED_GETTER" value="true" />
<option name="REPORT_NOT_ANNOTATED_SETTER_PARAMETER" value="true" />
<option name="REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS" value="true" />
<option name="REPORT_NULLS_PASSED_TO_NON_ANNOTATED_METHOD" value="true" />
</inspection_tool>
<inspection_tool class="UnqualifiedFieldAccess" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="ERRORS_ATTRIBUTES" /> <inspection_tool class="UnqualifiedFieldAccess" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="ERRORS_ATTRIBUTES" />
</profile> </profile>
</component> </component>

@ -1,3 +1,3 @@
# Enable auto-env through the sdkman_auto_env config # Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below # Add key=value pairs of SDKs to use below
java=17.0.8.1-librca java=8.0.382-librca

@ -0,0 +1 @@
java-baseline=8

@ -5,7 +5,6 @@
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions. 1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, "License" shall mean the terms and conditions for use, reproduction,

@ -1,4 +1,4 @@
= Spring Boot image:https://ci.spring.io/api/v1/teams/spring-boot/pipelines/spring-boot-3.2.x/jobs/build/badge["Build Status", link="https://ci.spring.io/teams/spring-boot/pipelines/spring-boot-3.2.x?groups=Build"] image:https://badges.gitter.im/Join Chat.svg["Chat",link="https://gitter.im/spring-projects/spring-boot?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"] image:https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Gradle Enterprise", link="https://ge.spring.io/scans?&search.rootProjectNames=Spring%20Boot%20Build&search.rootProjectNames=spring-boot-build"] = Spring Boot image:https://ci.spring.io/api/v1/teams/spring-boot/pipelines/spring-boot-2.7.x/jobs/build/badge["Build Status", link="https://ci.spring.io/teams/spring-boot/pipelines/spring-boot-2.7.x?groups=Build"] image:https://badges.gitter.im/Join Chat.svg["Chat",link="https://gitter.im/spring-projects/spring-boot?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"] image:https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Gradle Enterprise", link="https://ge.spring.io/scans?&search.rootProjectNames=Spring%20Boot%20Build&search.rootProjectNames=spring-boot-build"]
:docs: https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference :docs: https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference
:github: https://github.com/spring-projects/spring-boot :github: https://github.com/spring-projects/spring-boot
@ -10,7 +10,6 @@ We also provide a command-line tool that runs Spring scripts.
Our primary goals are: Our primary goals are:
* Provide a radically faster and widely accessible getting started experience for all Spring development. * Provide a radically faster and widely accessible getting started experience for all Spring development.
* Be opinionated, but get out of the way quickly as requirements start to diverge from the defaults. * Be opinionated, but get out of the way quickly as requirements start to diverge from the defaults.
* Provide a range of non-functional features common to large classes of projects (for example, embedded servers, security, metrics, health checks, externalized configuration). * Provide a range of non-functional features common to large classes of projects (for example, embedded servers, security, metrics, health checks, externalized configuration).
@ -76,7 +75,7 @@ We like to know the Spring Boot version, operating system, and JVM version you'r
== Building from Source == Building from Source
You don't need to build from source to use Spring Boot (binaries in https://repo.spring.io[repo.spring.io]), but if you want to try out the latest and greatest, Spring Boot can be built and published to your local Maven cache using the https://docs.gradle.org/current/userguide/gradle_wrapper.html[Gradle wrapper]. You don't need to build from source to use Spring Boot (binaries in https://repo.spring.io[repo.spring.io]), but if you want to try out the latest and greatest, Spring Boot can be built and published to your local Maven cache using the https://docs.gradle.org/current/userguide/gradle_wrapper.html[Gradle wrapper].
You also need JDK 17. You also need JDK 1.8.
[indent=0] [indent=0]
---- ----
@ -126,6 +125,12 @@ For example, if you want to get started using Spring and JPA for database access
=== spring-boot-cli
The Spring command-line application compiles and runs Groovy source, allowing you to write the absolute minimum amount of code to get an application running.
Spring CLI can also watch files, automatically recompiling and restarting when they change.
=== spring-boot-actuator === spring-boot-actuator
Actuator endpoints let you monitor and interact with your application. Actuator endpoints let you monitor and interact with your application.
Spring Boot Actuator provides the infrastructure required for actuator endpoints. Spring Boot Actuator provides the infrastructure required for actuator endpoints.
@ -165,6 +170,12 @@ Developer tools are automatically disabled when running a fully packaged applica
== Samples
Groovy samples for use with the command line application are available in link:spring-boot-project/spring-boot-cli/samples[spring-boot-cli/samples].
To run the CLI samples, type `spring run <sample>.groovy` from the samples directory.
== Guides == Guides
The https://spring.io/[spring.io] site contains several guides that show how to use Spring Boot step-by-step: The https://spring.io/[spring.io] site contains several guides that show how to use Spring Boot step-by-step:

@ -6,8 +6,6 @@ plugins {
description = "Spring Boot Build" description = "Spring Boot Build"
defaultTasks 'build' defaultTasks 'build'
nohttp { nohttp {

@ -2,7 +2,6 @@ plugins {
id "java-gradle-plugin" id "java-gradle-plugin"
id "io.spring.javaformat" version "${javaFormatVersion}" id "io.spring.javaformat" version "${javaFormatVersion}"
id "checkstyle" id "checkstyle"
id "eclipse"
} }
repositories { repositories {
@ -10,19 +9,18 @@ repositories {
gradlePluginPortal() gradlePluginPortal()
} }
sourceCompatibility = 17 sourceCompatibility = 1.8
targetCompatibility = 17 targetCompatibility = 1.8
def versions = [:] def versions = [:]
new File(projectDir.parentFile, "gradle.properties").withInputStream { new File(projectDir.parentFile, "gradle.properties").withInputStream {
def properties = new Properties() def properties = new Properties()
properties.load(it) properties.load(it)
["assertj", "commonsCodec", "hamcrest", "jackson", "junitJupiter", ["assertj", "commonsCodec", "hamcrest", "jackson", "junitJupiter",
"kotlin", "maven"].each { "kotlin", "maven", "springFramework"].each {
versions[it] = properties[it + "Version"] versions[it] = properties[it + "Version"]
} }
} }
versions["springFramework"] = "6.0.12"
ext.set("versions", versions) ext.set("versions", versions)
if (versions.springFramework.contains("-")) { if (versions.springFramework.contains("-")) {
repositories { repositories {
@ -35,7 +33,6 @@ dependencies {
checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${javaFormatVersion}" checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${javaFormatVersion}"
implementation(platform("org.springframework:spring-framework-bom:${versions.springFramework}")) implementation(platform("org.springframework:spring-framework-bom:${versions.springFramework}"))
implementation("com.diffplug.gradle:goomph:3.37.2")
implementation("com.fasterxml.jackson.core:jackson-databind:${versions.jackson}") implementation("com.fasterxml.jackson.core:jackson-databind:${versions.jackson}")
implementation("com.gradle:gradle-enterprise-gradle-plugin:3.12.1") implementation("com.gradle:gradle-enterprise-gradle-plugin:3.12.1")
implementation("com.tngtech.archunit:archunit:1.0.0") implementation("com.tngtech.archunit:archunit:1.0.0")
@ -59,7 +56,7 @@ dependencies {
} }
checkstyle { checkstyle {
toolVersion = "10.12.4" toolVersion = 9.3
} }
gradlePlugin { gradlePlugin {
@ -112,10 +109,6 @@ gradlePlugin {
id = "org.springframework.boot.optional-dependencies" id = "org.springframework.boot.optional-dependencies"
implementationClass = "org.springframework.boot.build.optional.OptionalDependenciesPlugin" implementationClass = "org.springframework.boot.build.optional.OptionalDependenciesPlugin"
} }
processedAnnotationsPlugin {
id = "org.springframework.boot.processed-annotations"
implementationClass = "org.springframework.boot.build.processors.ProcessedAnnotationsPlugin"
}
starterPlugin { starterPlugin {
id = "org.springframework.boot.starter" id = "org.springframework.boot.starter"
implementationClass = "org.springframework.boot.build.starters.StarterPlugin" implementationClass = "org.springframework.boot.build.starters.StarterPlugin"
@ -130,10 +123,3 @@ gradlePlugin {
test { test {
useJUnitPlatform() useJUnitPlatform()
} }
eclipse.classpath.file.whenMerged {
def jreEntry = entries.find { it.path.contains("org.eclipse.jdt.launching.JRE_CONTAINER") }
jreEntry.entryAttributes['module'] = 'true'
jreEntry.entryAttributes['limit-modules'] = 'java.base'
}

@ -106,10 +106,10 @@ class AsciidoctorConventions {
configureForkOptions(asciidoctorTask); configureForkOptions(asciidoctorTask);
asciidoctorTask.baseDirFollowsSourceDir(); asciidoctorTask.baseDirFollowsSourceDir();
createSyncDocumentationSourceTask(project, asciidoctorTask); createSyncDocumentationSourceTask(project, asciidoctorTask);
if (asciidoctorTask instanceof AsciidoctorTask task) { if (asciidoctorTask instanceof AsciidoctorTask) {
boolean pdf = task.getName().toLowerCase().contains("pdf"); boolean pdf = asciidoctorTask.getName().toLowerCase().contains("pdf");
String backend = (!pdf) ? "spring-html" : "spring-pdf"; String backend = (!pdf) ? "spring-html" : "spring-pdf";
task.outputOptions((outputOptions) -> outputOptions.backends(backend)); ((AsciidoctorTask) asciidoctorTask).outputOptions((outputOptions) -> outputOptions.backends(backend));
} }
} }
@ -134,7 +134,7 @@ class AsciidoctorConventions {
private String determineGitHubTag(Project project) { private String determineGitHubTag(Project project) {
String version = "v" + project.getVersion(); String version = "v" + project.getVersion();
return (version.endsWith("-SNAPSHOT")) ? "main" : version; return (version.endsWith("-SNAPSHOT")) ? "2.7.x" : version;
} }
private void configureOptions(AbstractAsciidoctorTask asciidoctorTask) { private void configureOptions(AbstractAsciidoctorTask asciidoctorTask) {

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -47,8 +47,6 @@ public class ConventionsPlugin implements Plugin<Project> {
new MavenPublishingConventions().apply(project); new MavenPublishingConventions().apply(project);
new AsciidoctorConventions().apply(project); new AsciidoctorConventions().apply(project);
new KotlinConventions().apply(project); new KotlinConventions().apply(project);
new WarConventions().apply(project);
new EclipseConventions().apply(project);
} }
} }

@ -1,69 +0,0 @@
/*
* Copyright 2023-2023 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.Project;
import org.gradle.plugins.ide.api.XmlFileContentMerger;
import org.gradle.plugins.ide.eclipse.EclipsePlugin;
import org.gradle.plugins.ide.eclipse.model.Classpath;
import org.gradle.plugins.ide.eclipse.model.ClasspathEntry;
import org.gradle.plugins.ide.eclipse.model.EclipseClasspath;
import org.gradle.plugins.ide.eclipse.model.EclipseModel;
import org.gradle.plugins.ide.eclipse.model.Library;
/**
* Conventions that are applied in the presence of the {@link EclipsePlugin} to work
* around buildship issue {@code #1238}.
*
* @author Phillip Webb
*/
class EclipseConventions {
void apply(Project project) {
project.getPlugins().withType(EclipsePlugin.class, (eclipse) -> {
EclipseModel eclipseModel = project.getExtensions().getByType(EclipseModel.class);
eclipseModel.classpath(this::configureClasspath);
});
}
private void configureClasspath(EclipseClasspath classpath) {
classpath.file(this::configureClasspathFile);
}
private void configureClasspathFile(XmlFileContentMerger merger) {
merger.whenMerged((content) -> {
if (content instanceof Classpath classpath) {
classpath.getEntries().removeIf(this::isKotlinPluginContributedBuildDirectory);
}
});
}
private boolean isKotlinPluginContributedBuildDirectory(ClasspathEntry entry) {
return (entry instanceof Library library) && isKotlinPluginContributedBuildDirectory(library.getPath())
&& isTest(library);
}
private boolean isKotlinPluginContributedBuildDirectory(String path) {
return path.contains("/main") && (path.contains("/build/classes/") || path.contains("/build/resources/"));
}
private boolean isTest(Library library) {
Object value = library.getEntryAttributes().get("test");
return (value instanceof String string && Boolean.parseBoolean(string));
}
}

@ -47,7 +47,6 @@ import org.gradle.api.tasks.bundling.Jar;
import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.api.tasks.javadoc.Javadoc; import org.gradle.api.tasks.javadoc.Javadoc;
import org.gradle.api.tasks.testing.Test; import org.gradle.api.tasks.testing.Test;
import org.gradle.external.javadoc.CoreJavadocOptions;
import org.springframework.boot.build.architecture.ArchitecturePlugin; import org.springframework.boot.build.architecture.ArchitecturePlugin;
import org.springframework.boot.build.classpath.CheckClasspathForProhibitedDependencies; import org.springframework.boot.build.classpath.CheckClasspathForProhibitedDependencies;
@ -61,7 +60,7 @@ import org.springframework.util.StringUtils;
* plugin is applied: * plugin is applied:
* *
* <ul> * <ul>
* <li>The project is configured with source and target compatibility of 17 * <li>The project is configured with source and target compatibility of 1.8
* <li>{@link SpringJavaFormatPlugin Spring Java Format}, {@link CheckstylePlugin * <li>{@link SpringJavaFormatPlugin Spring Java Format}, {@link CheckstylePlugin
* Checkstyle}, {@link TestFailuresPlugin Test Failures}, and {@link ArchitecturePlugin * Checkstyle}, {@link TestFailuresPlugin Test Failures}, and {@link ArchitecturePlugin
* Architecture} plugins are applied * Architecture} plugins are applied
@ -79,9 +78,9 @@ import org.springframework.util.StringUtils;
* {@link JavaPlugin} applied * {@link JavaPlugin} applied
* <li>{@link JavaCompile}, {@link Javadoc}, and {@link Format} tasks are configured to * <li>{@link JavaCompile}, {@link Javadoc}, and {@link Format} tasks are configured to
* use UTF-8 encoding * use UTF-8 encoding
* <li>{@link JavaCompile} tasks are configured to: * <li>{@link JavaCompile} tasks are configured to use {@code -parameters}.
* <li>When building with Java 8, {@link JavaCompile} tasks are also configured to:
* <ul> * <ul>
* <li>Use {@code -parameters}.
* <li>Treat warnings as errors * <li>Treat warnings as errors
* <li>Enable {@code unchecked}, {@code deprecation}, {@code rawtypes}, and {@code varags} * <li>Enable {@code unchecked}, {@code deprecation}, {@code rawtypes}, and {@code varags}
* warnings * warnings
@ -107,7 +106,7 @@ import org.springframework.util.StringUtils;
*/ */
class JavaConventions { class JavaConventions {
private static final String SOURCE_AND_TARGET_COMPATIBILITY = "17"; private static final String SOURCE_AND_TARGET_COMPATIBILITY = "1.8";
void apply(Project project) { void apply(Project project) {
project.getPlugins().withType(JavaBasePlugin.class, (java) -> { project.getPlugins().withType(JavaBasePlugin.class, (java) -> {
@ -200,12 +199,7 @@ class JavaConventions {
} }
private void configureJavadocConventions(Project project) { private void configureJavadocConventions(Project project) {
project.getTasks().withType(Javadoc.class, (javadoc) -> { project.getTasks().withType(Javadoc.class, (javadoc) -> javadoc.getOptions().source("1.8").encoding("UTF-8"));
CoreJavadocOptions options = (CoreJavadocOptions) javadoc.getOptions();
options.source("17");
options.encoding("UTF-8");
options.addStringOption("Xdoclint:none", "-quiet");
});
} }
private void configureJavaConventions(Project project) { private void configureJavaConventions(Project project) {
@ -223,15 +217,15 @@ class JavaConventions {
compile.setSourceCompatibility(SOURCE_AND_TARGET_COMPATIBILITY); compile.setSourceCompatibility(SOURCE_AND_TARGET_COMPATIBILITY);
compile.setTargetCompatibility(SOURCE_AND_TARGET_COMPATIBILITY); compile.setTargetCompatibility(SOURCE_AND_TARGET_COMPATIBILITY);
} }
else if (buildingWithJava17(project)) { else if (buildingWithJava8(project)) {
args.addAll(Arrays.asList("-Werror", "-Xlint:unchecked", "-Xlint:deprecation", "-Xlint:rawtypes", args.addAll(Arrays.asList("-Werror", "-Xlint:unchecked", "-Xlint:deprecation", "-Xlint:rawtypes",
"-Xlint:varargs")); "-Xlint:varargs"));
} }
}); });
} }
private boolean buildingWithJava17(Project project) { private boolean buildingWithJava8(Project project) {
return !project.hasProperty("toolchainVersion") && JavaVersion.current() == JavaVersion.VERSION_17; return !project.hasProperty("toolchainVersion") && JavaVersion.current() == JavaVersion.VERSION_1_8;
} }
private void configureSpringJavaFormat(Project project) { private void configureSpringJavaFormat(Project project) {
@ -258,10 +252,11 @@ class JavaConventions {
.matching((configuration) -> configuration.getName().endsWith("Classpath") .matching((configuration) -> configuration.getName().endsWith("Classpath")
|| JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME.equals(configuration.getName())) || JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME.equals(configuration.getName()))
.all((configuration) -> configuration.extendsFrom(dependencyManagement)); .all((configuration) -> configuration.extendsFrom(dependencyManagement));
Dependency springBootParent = project.getDependencies() String path = project.getName().contains("spring-boot-starter")
.enforcedPlatform(project.getDependencies() ? ":spring-boot-project:spring-boot-dependencies" : ":spring-boot-project:spring-boot-parent";
.project(Collections.singletonMap("path", ":spring-boot-project:spring-boot-parent"))); Dependency dependency = project.getDependencies()
dependencyManagement.getDependencies().add(springBootParent); .enforcedPlatform(project.getDependencies().project(Collections.singletonMap("path", path)));
dependencyManagement.getDependencies().add(dependency);
project.getPlugins() project.getPlugins()
.withType(OptionalDependenciesPlugin.class, .withType(OptionalDependenciesPlugin.class,
(optionalDependencies) -> configurations (optionalDependencies) -> configurations

@ -30,8 +30,8 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile;
* <ul> * <ul>
* <li>{@link KotlinCompile} tasks are configured to: * <li>{@link KotlinCompile} tasks are configured to:
* <ul> * <ul>
* <li>Use {@code apiVersion} and {@code languageVersion} 1.7. * <li>Use {@code apiVersion} and {@code languageVersion} 1.3.
* <li>Use {@code jvmTarget} 17. * <li>Use {@code jvmTarget} 1.8.
* <li>Treat all warnings as errors * <li>Treat all warnings as errors
* <li>Suppress version warnings * <li>Suppress version warnings
* </ul> * </ul>
@ -51,9 +51,9 @@ class KotlinConventions {
private void configure(KotlinCompile compile) { private void configure(KotlinCompile compile) {
KotlinJvmOptions kotlinOptions = compile.getKotlinOptions(); KotlinJvmOptions kotlinOptions = compile.getKotlinOptions();
kotlinOptions.setApiVersion("1.7"); kotlinOptions.setApiVersion("1.3");
kotlinOptions.setLanguageVersion("1.7"); kotlinOptions.setLanguageVersion("1.3");
kotlinOptions.setJvmTarget("17"); kotlinOptions.setJvmTarget("1.8");
kotlinOptions.setAllWarningsAsErrors(true); kotlinOptions.setAllWarningsAsErrors(true);
List<String> freeCompilerArgs = new ArrayList<>(compile.getKotlinOptions().getFreeCompilerArgs()); List<String> freeCompilerArgs = new ArrayList<>(compile.getKotlinOptions().getFreeCompilerArgs());
freeCompilerArgs.add("-Xsuppress-version-warnings"); freeCompilerArgs.add("-Xsuppress-version-warnings");

@ -1,60 +0,0 @@
/*
* Copyright 2023-2023 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.ArrayList;
import java.util.List;
import org.gradle.api.JavaVersion;
import org.gradle.api.Project;
import org.gradle.api.internal.IConventionAware;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.plugins.ide.eclipse.EclipseWtpPlugin;
import org.gradle.plugins.ide.eclipse.model.EclipseModel;
import org.gradle.plugins.ide.eclipse.model.Facet;
/**
* Conventions that are applied in the presence of the {WarPlugin}. When the plugin is
* applied:
* <ul>
* <li>Update Eclipse WTP Plugin facets to use Servlet 5.0</li>
* </ul>
*
* @author Phillip Webb
*/
public class WarConventions {
void apply(Project project) {
project.getPlugins().withType(EclipseWtpPlugin.class, (wtp) -> {
project.getTasks().getByName(EclipseWtpPlugin.ECLIPSE_WTP_FACET_TASK_NAME).doFirst((task) -> {
EclipseModel eclipseModel = project.getExtensions().getByType(EclipseModel.class);
((IConventionAware) eclipseModel.getWtp().getFacet()).getConventionMapping()
.map("facets", () -> getFacets(project));
});
});
}
private List<Facet> getFacets(Project project) {
JavaVersion javaVersion = project.getExtensions().getByType(JavaPluginExtension.class).getSourceCompatibility();
List<Facet> facets = new ArrayList<>();
facets.add(new Facet(Facet.FacetType.fixed, "jst.web", null));
facets.add(new Facet(Facet.FacetType.installed, "jst.web", "5.0"));
facets.add(new Facet(Facet.FacetType.installed, "jst.java", javaVersion.toString()));
return facets;
}
}

@ -18,6 +18,7 @@ package org.springframework.boot.build.architecture;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.util.List; import java.util.List;
@ -71,19 +72,20 @@ public abstract class ArchitectureCheck extends DefaultTask {
allBeanPostProcessorBeanMethodsShouldBeStaticAndHaveParametersThatWillNotCausePrematureInitialization(), allBeanPostProcessorBeanMethodsShouldBeStaticAndHaveParametersThatWillNotCausePrematureInitialization(),
allBeanFactoryPostProcessorBeanMethodsShouldBeStaticAndHaveNoParameters(), allBeanFactoryPostProcessorBeanMethodsShouldBeStaticAndHaveNoParameters(),
noClassesShouldCallStepVerifierStepVerifyComplete(), noClassesShouldCallStepVerifierStepVerifyComplete(),
noClassesShouldConfigureDefaultStepVerifierTimeout(), noClassesShouldCallCollectorsToList()); noClassesShouldConfigureDefaultStepVerifierTimeout());
getRuleDescriptions().set(getRules().map((rules) -> rules.stream().map(ArchRule::getDescription).toList())); getRuleDescriptions()
.set(getRules().map((rules) -> rules.stream().map(ArchRule::getDescription).collect(Collectors.toList())));
} }
@TaskAction @TaskAction
void checkArchitecture() throws IOException { void checkArchitecture() throws IOException {
JavaClasses javaClasses = new ClassFileImporter() JavaClasses javaClasses = new ClassFileImporter()
.importPaths(this.classes.getFiles().stream().map(File::toPath).toList()); .importPaths(this.classes.getFiles().stream().map(File::toPath).collect(Collectors.toList()));
List<EvaluationResult> violations = getRules().get() List<EvaluationResult> violations = getRules().get()
.stream() .stream()
.map((rule) -> rule.evaluate(javaClasses)) .map((rule) -> rule.evaluate(javaClasses))
.filter(EvaluationResult::hasViolation) .filter(EvaluationResult::hasViolation)
.toList(); .collect(Collectors.toList());
File outputFile = getOutputDirectory().file("failure-report.txt").get().getAsFile(); File outputFile = getOutputDirectory().file("failure-report.txt").get().getAsFile();
outputFile.getParentFile().mkdirs(); outputFile.getParentFile().mkdirs();
if (!violations.isEmpty()) { if (!violations.isEmpty()) {
@ -92,8 +94,8 @@ public abstract class ArchitectureCheck extends DefaultTask {
report.append(violation.getFailureReport().toString()); report.append(violation.getFailureReport().toString());
report.append(String.format("%n")); report.append(String.format("%n"));
} }
Files.writeString(outputFile.toPath(), report.toString(), StandardOpenOption.CREATE, Files.write(outputFile.toPath(), report.toString().getBytes(StandardCharsets.UTF_8),
StandardOpenOption.TRUNCATE_EXISTING); StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
throw new GradleException("Architecture check failed. See '" + outputFile + "' for details."); throw new GradleException("Architecture check failed. See '" + outputFile + "' for details.");
} }
else { else {
@ -124,7 +126,7 @@ public abstract class ArchitectureCheck extends DefaultTask {
.not(Predicates.assignableTo("org.springframework.beans.factory.ObjectProvider") .not(Predicates.assignableTo("org.springframework.beans.factory.ObjectProvider")
.or(Predicates.assignableTo("org.springframework.context.ApplicationContext")) .or(Predicates.assignableTo("org.springframework.context.ApplicationContext"))
.or(Predicates.assignableTo("org.springframework.core.env.Environment"))); .or(Predicates.assignableTo("org.springframework.core.env.Environment")));
return new ArchCondition<>("not have parameters that will cause eager initialization") { return new ArchCondition<JavaMethod>("not have parameters that will cause eager initialization") {
@Override @Override
public void check(JavaMethod item, ConditionEvents events) { public void check(JavaMethod item, ConditionEvents events) {
@ -155,7 +157,7 @@ public abstract class ArchitectureCheck extends DefaultTask {
} }
private ArchCondition<JavaMethod> haveNoParameters() { private ArchCondition<JavaMethod> haveNoParameters() {
return new ArchCondition<>("have no parameters") { return new ArchCondition<JavaMethod>("have no parameters") {
@Override @Override
public void check(JavaMethod item, ConditionEvents events) { public void check(JavaMethod item, ConditionEvents events) {
@ -183,13 +185,6 @@ public abstract class ArchitectureCheck extends DefaultTask {
.because("expectComplete().verify(Duration) should be used instead"); .because("expectComplete().verify(Duration) should be used instead");
} }
private ArchRule noClassesShouldCallCollectorsToList() {
return ArchRuleDefinition.noClasses()
.should()
.callMethod(Collectors.class, "toList")
.because("java.util.stream.Stream.toList() should be used instead");
}
public void setClasses(FileCollection classes) { public void setClasses(FileCollection classes) {
this.classes = classes; this.classes = classes;
} }

@ -23,6 +23,9 @@ import java.io.FileReader;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
@ -40,13 +43,13 @@ import org.gradle.api.tasks.TaskAction;
import org.springframework.asm.ClassReader; import org.springframework.asm.ClassReader;
import org.springframework.asm.Opcodes; import org.springframework.asm.Opcodes;
import org.springframework.core.CollectionFactory; import org.springframework.core.CollectionFactory;
import org.springframework.util.StringUtils;
/** /**
* A {@link Task} for generating metadata describing a project's auto-configuration * A {@link Task} for generating metadata describing a project's auto-configuration
* classes. * classes.
* *
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Scott Frederick
*/ */
public class AutoConfigurationMetadata extends DefaultTask { public class AutoConfigurationMetadata extends DefaultTask {
@ -57,6 +60,11 @@ public class AutoConfigurationMetadata extends DefaultTask {
private File outputFile; private File outputFile;
public AutoConfigurationMetadata() { public AutoConfigurationMetadata() {
getInputs()
.file((Callable<File>) () -> new File(this.sourceSet.getOutput().getResourcesDir(),
"META-INF/spring.factories"))
.withPathSensitivity(PathSensitivity.RELATIVE)
.withPropertyName("spring.factories");
getInputs() getInputs()
.file((Callable<File>) () -> new File(this.sourceSet.getOutput().getResourcesDir(), .file((Callable<File>) () -> new File(this.sourceSet.getOutput().getResourcesDir(),
"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports")) "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports"))
@ -92,7 +100,9 @@ public class AutoConfigurationMetadata extends DefaultTask {
private Properties readAutoConfiguration() throws IOException { private Properties readAutoConfiguration() throws IOException {
Properties autoConfiguration = CollectionFactory.createSortedProperties(true); Properties autoConfiguration = CollectionFactory.createSortedProperties(true);
List<String> classNames = readAutoConfigurationsFile(); Set<String> classNames = new LinkedHashSet<>();
classNames.addAll(readSpringFactories());
classNames.addAll(readAutoConfigurationsFile());
Set<String> publicClassNames = new LinkedHashSet<>(); Set<String> publicClassNames = new LinkedHashSet<>();
for (String className : classNames) { for (String className : classNames) {
File classFile = findClassFile(className); File classFile = findClassFile(className);
@ -111,6 +121,21 @@ public class AutoConfigurationMetadata extends DefaultTask {
return autoConfiguration; return autoConfiguration;
} }
/**
* Reads auto-configurations from META-INF/spring.factories.
* @return auto-configurations
*/
private Set<String> readSpringFactories() throws IOException {
File file = new File(this.sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories");
if (!file.exists()) {
return Collections.emptySet();
}
Properties springFactories = readSpringFactories(file);
String enableAutoConfiguration = springFactories
.getProperty("org.springframework.boot.autoconfigure.EnableAutoConfiguration");
return StringUtils.commaDelimitedListToSet(enableAutoConfiguration);
}
/** /**
* Reads auto-configurations from * Reads auto-configurations from
* META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. * META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.
@ -122,17 +147,29 @@ public class AutoConfigurationMetadata extends DefaultTask {
if (!file.exists()) { if (!file.exists()) {
return Collections.emptyList(); return Collections.emptyList();
} }
try (BufferedReader reader = new BufferedReader(new FileReader(file))) { // Nearly identical copy of
return reader.lines().map(this::stripComment).filter((line) -> !line.isEmpty()).toList(); // org.springframework.boot.context.annotation.ImportCandidates.load
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) {
List<String> autoConfigurations = new ArrayList<>();
String line;
while ((line = reader.readLine()) != null) {
line = stripComment(line);
line = line.trim();
if (line.isEmpty()) {
continue;
}
autoConfigurations.add(line);
}
return autoConfigurations;
} }
} }
private String stripComment(String line) { private String stripComment(String line) {
int commentStart = line.indexOf(COMMENT_START); int commentStart = line.indexOf(COMMENT_START);
if (commentStart == -1) { if (commentStart == -1) {
return line.trim(); return line;
} }
return line.substring(0, commentStart).trim(); return line.substring(0, commentStart);
} }
private File findClassFile(String className) { private File findClassFile(String className) {
@ -146,4 +183,12 @@ public class AutoConfigurationMetadata extends DefaultTask {
return null; return null;
} }
private Properties readSpringFactories(File file) throws IOException {
Properties springFactories = new Properties();
try (Reader in = new FileReader(file)) {
springFactories.load(in);
}
return springFactories;
}
} }

@ -42,6 +42,7 @@ import org.gradle.api.tasks.SourceSet;
import org.springframework.boot.build.DeployedPlugin; import org.springframework.boot.build.DeployedPlugin;
import org.springframework.boot.build.architecture.ArchitectureCheck; import org.springframework.boot.build.architecture.ArchitectureCheck;
import org.springframework.boot.build.architecture.ArchitecturePlugin; import org.springframework.boot.build.architecture.ArchitecturePlugin;
import org.springframework.boot.build.context.properties.ConfigurationPropertiesPlugin;
/** /**
* {@link Plugin} for projects that define auto-configuration. When applied, the plugin * {@link Plugin} for projects that define auto-configuration. When applied, the plugin
@ -49,6 +50,7 @@ import org.springframework.boot.build.architecture.ArchitecturePlugin;
* applied it: * applied it:
* *
* <ul> * <ul>
* <li>Applies the {@link ConfigurationPropertiesPlugin}.
* <li>Adds a dependency on the auto-configuration annotation processor. * <li>Adds a dependency on the auto-configuration annotation processor.
* <li>Defines a task that produces metadata describing the auto-configuration. The * <li>Defines a task that produces metadata describing the auto-configuration. The
* metadata is made available as an artifact in the {@code autoConfigurationMetadata} * metadata is made available as an artifact in the {@code autoConfigurationMetadata}
@ -77,6 +79,7 @@ public class AutoConfigurationPlugin implements Plugin<Project> {
public void apply(Project project) { public void apply(Project project) {
project.getPlugins().apply(DeployedPlugin.class); project.getPlugins().apply(DeployedPlugin.class);
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> { project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
project.getPlugins().apply(ConfigurationPropertiesPlugin.class);
Configuration annotationProcessors = project.getConfigurations() Configuration annotationProcessors = project.getConfigurations()
.getByName(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME); .getByName(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME);
annotationProcessors.getDependencies() annotationProcessors.getDependencies()
@ -127,7 +130,7 @@ public class AutoConfigurationPlugin implements Plugin<Project> {
} }
private ArchCondition<JavaClass> beListedInAutoConfigurationImports(Provider<AutoConfigurationImports> imports) { private ArchCondition<JavaClass> beListedInAutoConfigurationImports(Provider<AutoConfigurationImports> imports) {
return new ArchCondition<>("be listed in " + AUTO_CONFIGURATION_IMPORTS_PATH) { return new ArchCondition<JavaClass>("be listed in " + AUTO_CONFIGURATION_IMPORTS_PATH) {
@Override @Override
public void check(JavaClass item, ConditionEvents events) { public void check(JavaClass item, ConditionEvents events) {
@ -154,7 +157,16 @@ public class AutoConfigurationPlugin implements Plugin<Project> {
}); });
} }
private static record AutoConfigurationImports(Path importsFile, List<String> imports) { private static final class AutoConfigurationImports {
private final Path importsFile;
private final List<String> imports;
private AutoConfigurationImports(Path importsFile, List<String> imports) {
this.importsFile = importsFile;
this.imports = imports;
}
} }

@ -26,6 +26,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
@ -105,7 +106,7 @@ public class BomExtension {
} }
public void library(String name, Action<LibraryHandler> action) { public void library(String name, Action<LibraryHandler> action) {
library(name, null, action); this.library(name, null, action);
} }
public void library(String name, String version, Action<LibraryHandler> action) { public void library(String name, String version, Action<LibraryHandler> action) {
@ -318,8 +319,8 @@ public class BomExtension {
public void setModules(List<Object> modules) { public void setModules(List<Object> modules) {
this.modules = modules.stream() this.modules = modules.stream()
.map((input) -> (input instanceof Module module) ? module : new Module((String) input)) .map((input) -> (input instanceof Module) ? (Module) input : new Module((String) input))
.toList(); .collect(Collectors.toList());
} }
public void setImports(List<String> imports) { public void setImports(List<String> imports) {
@ -333,8 +334,9 @@ public class BomExtension {
public Object methodMissing(String name, Object args) { public Object methodMissing(String name, Object args) {
if (args instanceof Object[] && ((Object[]) args).length == 1) { if (args instanceof Object[] && ((Object[]) args).length == 1) {
Object arg = ((Object[]) args)[0]; Object arg = ((Object[]) args)[0];
if (arg instanceof Closure<?> closure) { if (arg instanceof Closure) {
ModuleHandler moduleHandler = new ModuleHandler(); ModuleHandler moduleHandler = new ModuleHandler();
Closure<?> closure = (Closure<?>) arg;
closure.setResolveStrategy(Closure.DELEGATE_FIRST); closure.setResolveStrategy(Closure.DELEGATE_FIRST);
closure.setDelegate(moduleHandler); closure.setDelegate(moduleHandler);
closure.call(moduleHandler); closure.call(moduleHandler);

@ -268,8 +268,9 @@ public class BomPlugin implements Plugin<Project> {
private Node findChild(Node parent, String name) { private Node findChild(Node parent, String name) {
for (Object child : parent.children()) { for (Object child : parent.children()) {
if (child instanceof Node node) { if (child instanceof Node) {
if ((node.name() instanceof QName qname) && name.equals(qname.getLocalPart())) { Node node = (Node) child;
if ((node.name() instanceof QName) && name.equals(((QName) node.name()).getLocalPart())) {
return node; return node;
} }
if (name.equals(node.name())) { if (name.equals(node.name())) {
@ -282,12 +283,17 @@ public class BomPlugin implements Plugin<Project> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private List<Node> findChildren(Node parent, String name) { private List<Node> findChildren(Node parent, String name) {
return parent.children().stream().filter((child) -> isNodeWithName(child, name)).toList(); return (List<Node>) parent.children()
.stream()
.filter((child) -> isNodeWithName(child, name))
.collect(Collectors.toList());
} }
private boolean isNodeWithName(Object candidate, String name) { private boolean isNodeWithName(Object candidate, String name) {
if (candidate instanceof Node node) { if (candidate instanceof Node) {
if ((node.name() instanceof QName qname) && name.equals(qname.getLocalPart())) { Node node = (Node) candidate;
if ((node.name() instanceof QName) && name.equals(((QName) node.name()).getLocalPart())) {
return true; return true;
} }
if (name.equals(node.name())) { if (name.equals(node.name())) {

@ -21,6 +21,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors;
import org.gradle.api.internal.tasks.userinput.UserInputHandler; import org.gradle.api.internal.tasks.userinput.UserInputHandler;
@ -51,7 +52,7 @@ public final class InteractiveUpgradeResolver implements UpgradeResolver {
} }
List<LibraryWithVersionOptions> libraryUpdates = this.libraryUpdateResolver List<LibraryWithVersionOptions> libraryUpdates = this.libraryUpdateResolver
.findLibraryUpdates(librariesToUpgrade, librariesByName); .findLibraryUpdates(librariesToUpgrade, librariesByName);
return libraryUpdates.stream().map(this::resolveUpgrade).filter(Objects::nonNull).toList(); return libraryUpdates.stream().map(this::resolveUpgrade).filter(Objects::nonNull).collect(Collectors.toList());
} }
private Upgrade resolveUpgrade(LibraryWithVersionOptions libraryWithVersionOptions) { private Upgrade resolveUpgrade(LibraryWithVersionOptions libraryWithVersionOptions) {

@ -58,18 +58,7 @@ final class MavenMetadataVersionResolver implements VersionResolver {
MavenMetadataVersionResolver(RestTemplate restTemplate, Collection<URI> repositoryUrls) { MavenMetadataVersionResolver(RestTemplate restTemplate, Collection<URI> repositoryUrls) {
this.rest = restTemplate; this.rest = restTemplate;
this.repositoryUrls = normalize(repositoryUrls); this.repositoryUrls = repositoryUrls;
}
private Collection<URI> normalize(Collection<URI> uris) {
return uris.stream().map(this::normalize).toList();
}
private URI normalize(URI uri) {
if ("/".equals(uri.getPath())) {
return uri;
}
return URI.create(uri.toString() + "/");
} }
@Override @Override

@ -24,6 +24,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -61,7 +62,7 @@ class MultithreadedLibraryUpdateResolver implements LibraryUpdateResolver {
.map((library) -> executorService.submit( .map((library) -> executorService.submit(
() -> this.delegate.findLibraryUpdates(Collections.singletonList(library), librariesByName))) () -> this.delegate.findLibraryUpdates(Collections.singletonList(library), librariesByName)))
.flatMap(this::getResult) .flatMap(this::getResult)
.toList(); .collect(Collectors.toList());
} }
finally { finally {
executorService.shutdownNow(); executorService.shutdownNow();

@ -19,11 +19,13 @@ package org.springframework.boot.build.bom.bomr;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.function.BiPredicate; import java.util.function.BiPredicate;
import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -94,14 +96,19 @@ class StandardLibraryUpdateResolver implements LibraryUpdateResolver {
getLaterVersionsForModule(group.getId(), plugin, library)); getLaterVersionsForModule(group.getId(), plugin, library));
} }
} }
return moduleVersions.values() List<DependencyVersion> allVersions = moduleVersions.values()
.stream() .stream()
.flatMap(SortedSet::stream) .flatMap(SortedSet::stream)
.distinct() .distinct()
.filter((dependencyVersion) -> this.predicate.test(library, dependencyVersion)) .filter((dependencyVersion) -> this.predicate.test(library, dependencyVersion))
.map((version) -> (VersionOption) new VersionOption.ResolvedVersionOption(version, .collect(Collectors.toList());
if (allVersions.isEmpty()) {
return Collections.emptyList();
}
return allVersions.stream()
.map((version) -> new VersionOption.ResolvedVersionOption(version,
getMissingModules(moduleVersions, version))) getMissingModules(moduleVersions, version)))
.toList(); .collect(Collectors.toList());
} }
private List<String> getMissingModules(Map<String, SortedSet<DependencyVersion>> moduleVersions, private List<String> getMissingModules(Map<String, SortedSet<DependencyVersion>> moduleVersions,

@ -17,6 +17,7 @@
package org.springframework.boot.build.bom.bomr; package org.springframework.boot.build.bom.bomr;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
@ -41,7 +42,7 @@ class UpgradeApplicator {
} }
Path apply(Upgrade upgrade) throws IOException { Path apply(Upgrade upgrade) throws IOException {
String buildFileContents = Files.readString(this.buildFile); String buildFileContents = new String(Files.readAllBytes(this.buildFile), StandardCharsets.UTF_8);
Matcher matcher = Pattern.compile("library\\(\"" + upgrade.getLibrary().getName() + "\", \"(.+)\"\\)") Matcher matcher = Pattern.compile("library\\(\"" + upgrade.getLibrary().getName() + "\", \"(.+)\"\\)")
.matcher(buildFileContents); .matcher(buildFileContents);
if (!matcher.find()) { if (!matcher.find()) {
@ -67,7 +68,7 @@ class UpgradeApplicator {
private void updateGradleProperties(Upgrade upgrade, String version) throws IOException { private void updateGradleProperties(Upgrade upgrade, String version) throws IOException {
String property = version.substring(2, version.length() - 1); String property = version.substring(2, version.length() - 1);
String gradlePropertiesContents = Files.readString(this.gradleProperties); String gradlePropertiesContents = new String(Files.readAllBytes(this.gradleProperties), StandardCharsets.UTF_8);
String modified = gradlePropertiesContents.replace( String modified = gradlePropertiesContents.replace(
property + "=" + upgrade.getLibrary().getVersion().getVersion(), property + "=" + upgrade.getVersion()); property + "=" + upgrade.getLibrary().getVersion().getVersion(), property + "=" + upgrade.getVersion());
overwrite(this.gradleProperties, modified); overwrite(this.gradleProperties, modified);
@ -81,7 +82,8 @@ class UpgradeApplicator {
} }
private void overwrite(Path target, String content) throws IOException { private void overwrite(Path target, String content) throws IOException {
Files.writeString(target, content, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING); Files.write(target, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE,
StandardOpenOption.TRUNCATE_EXISTING);
} }
} }

@ -30,6 +30,7 @@ import java.util.Set;
import java.util.function.BiPredicate; import java.util.function.BiPredicate;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
@ -260,7 +261,10 @@ public abstract class UpgradeDependencies extends DefaultTask {
} }
private List<Library> matchingLibraries() { private List<Library> matchingLibraries() {
List<Library> matchingLibraries = this.bom.getLibraries().stream().filter(this::eligible).toList(); List<Library> matchingLibraries = this.bom.getLibraries()
.stream()
.filter(this::eligible)
.collect(Collectors.toList());
if (matchingLibraries.isEmpty()) { if (matchingLibraries.isEmpty()) {
throw new InvalidUserDataException("No libraries to upgrade"); throw new InvalidUserDataException("No libraries to upgrade");
} }

@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException.Forbidden; import org.springframework.web.client.HttpClientErrorException.Forbidden;
@ -62,8 +63,8 @@ final class StandardGitHubRepository implements GitHubRepository {
return (Integer) response.getBody().get("number"); return (Integer) response.getBody().get("number");
} }
catch (RestClientException ex) { catch (RestClientException ex) {
if (ex instanceof Forbidden forbidden) { if (ex instanceof Forbidden) {
System.out.println("Received 403 response with headers " + forbidden.getResponseHeaders()); System.out.println("Received 403 response with headers " + ((Forbidden) ex).getResponseHeaders());
} }
throw ex; throw ex;
} }
@ -93,7 +94,8 @@ final class StandardGitHubRepository implements GitHubRepository {
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
private <T> List<T> get(String name, Function<Map<String, Object>, T> mapper) { private <T> List<T> get(String name, Function<Map<String, Object>, T> mapper) {
ResponseEntity<List> response = this.rest.getForEntity(name, List.class); ResponseEntity<List> response = this.rest.getForEntity(name, List.class);
return ((List<Map<String, Object>>) response.getBody()).stream().map(mapper).toList(); List<Map<String, Object>> body = response.getBody();
return body.stream().map(mapper).collect(Collectors.toList());
} }
private static void sleep(Duration duration) { private static void sleep(Duration duration) {

@ -33,8 +33,8 @@ abstract class AbstractDependencyVersion implements DependencyVersion {
@Override @Override
public int compareTo(DependencyVersion other) { public int compareTo(DependencyVersion other) {
ComparableVersion otherComparable = (other instanceof AbstractDependencyVersion otherVersion) ComparableVersion otherComparable = (other instanceof AbstractDependencyVersion)
? otherVersion.comparableVersion : new ComparableVersion(other.toString()); ? ((AbstractDependencyVersion) other).comparableVersion : new ComparableVersion(other.toString());
return this.comparableVersion.compareTo(otherComparable); return this.comparableVersion.compareTo(otherComparable);
} }

@ -16,7 +16,6 @@
package org.springframework.boot.build.bom.bomr.version; package org.springframework.boot.build.bom.bomr.version;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import org.apache.maven.artifact.versioning.ArtifactVersion; import org.apache.maven.artifact.versioning.ArtifactVersion;
@ -122,21 +121,6 @@ class ArtifactVersionDependencyVersion extends AbstractDependencyVersion {
return sameMajorMinorIncremental(((ArtifactVersionDependencyVersion) candidate).artifactVersion); return sameMajorMinorIncremental(((ArtifactVersionDependencyVersion) candidate).artifactVersion);
} }
@Override
public int compareTo(DependencyVersion other) {
if (other instanceof ArtifactVersionDependencyVersion otherArtifactDependencyVersion) {
ArtifactVersion otherArtifactVersion = otherArtifactDependencyVersion.artifactVersion;
if ((!Objects.equals(this.artifactVersion.getQualifier(), otherArtifactVersion.getQualifier()))
&& "snapshot".equalsIgnoreCase(otherArtifactVersion.getQualifier())
&& otherArtifactVersion.getMajorVersion() == this.artifactVersion.getMajorVersion()
&& otherArtifactVersion.getMinorVersion() == this.artifactVersion.getMinorVersion()
&& otherArtifactVersion.getIncrementalVersion() == this.artifactVersion.getIncrementalVersion()) {
return 1;
}
}
return super.compareTo(other);
}
@Override @Override
public String toString() { public String toString() {
return this.artifactVersion.toString(); return this.artifactVersion.toString();
@ -145,8 +129,8 @@ class ArtifactVersionDependencyVersion extends AbstractDependencyVersion {
protected Optional<ArtifactVersionDependencyVersion> extractArtifactVersionDependencyVersion( protected Optional<ArtifactVersionDependencyVersion> extractArtifactVersionDependencyVersion(
DependencyVersion other) { DependencyVersion other) {
ArtifactVersionDependencyVersion artifactVersion = null; ArtifactVersionDependencyVersion artifactVersion = null;
if (other instanceof ArtifactVersionDependencyVersion otherVersion) { if (other instanceof ArtifactVersionDependencyVersion) {
artifactVersion = otherVersion; artifactVersion = (ArtifactVersionDependencyVersion) other;
} }
return Optional.ofNullable(artifactVersion); return Optional.ofNullable(artifactVersion);
} }

@ -48,9 +48,10 @@ final class ReleaseTrainDependencyVersion implements DependencyVersion {
@Override @Override
public int compareTo(DependencyVersion other) { public int compareTo(DependencyVersion other) {
if (!(other instanceof ReleaseTrainDependencyVersion otherReleaseTrain)) { if (!(other instanceof ReleaseTrainDependencyVersion)) {
return -1; return -1;
} }
ReleaseTrainDependencyVersion otherReleaseTrain = (ReleaseTrainDependencyVersion) other;
int comparison = this.releaseTrain.compareTo(otherReleaseTrain.releaseTrain); int comparison = this.releaseTrain.compareTo(otherReleaseTrain.releaseTrain);
if (comparison != 0) { if (comparison != 0) {
return comparison; return comparison;
@ -109,11 +110,12 @@ final class ReleaseTrainDependencyVersion implements DependencyVersion {
if (other instanceof CalendarVersionDependencyVersion) { if (other instanceof CalendarVersionDependencyVersion) {
return false; return false;
} }
if (other instanceof ReleaseTrainDependencyVersion otherReleaseTrain) { if (!(other instanceof ReleaseTrainDependencyVersion)) {
return otherReleaseTrain.releaseTrain.equals(this.releaseTrain);
}
return true; return true;
} }
ReleaseTrainDependencyVersion otherReleaseTrain = (ReleaseTrainDependencyVersion) other;
return otherReleaseTrain.releaseTrain.equals(this.releaseTrain);
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {

@ -34,7 +34,6 @@ import java.util.function.Predicate;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.gradle.api.DefaultTask; import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException; import org.gradle.api.GradleException;
@ -69,11 +68,10 @@ public class CheckClasspathForConflicts extends DefaultTask {
for (File file : this.classpath) { for (File file : this.classpath) {
if (file.isDirectory()) { if (file.isDirectory()) {
Path root = file.toPath(); Path root = file.toPath();
try (Stream<Path> pathStream = Files.walk(root)) { Files.walk(root)
pathStream.filter(Files::isRegularFile) .filter(Files::isRegularFile)
.forEach((entry) -> classpathContents.add(root.relativize(entry).toString(), root.toString())); .forEach((entry) -> classpathContents.add(root.relativize(entry).toString(), root.toString()));
} }
}
else { else {
try (JarFile jar = new JarFile(file)) { try (JarFile jar = new JarFile(file)) {
for (JarEntry entry : Collections.list(jar.entries())) { for (JarEntry entry : Collections.list(jar.entries())) {

@ -79,12 +79,6 @@ public class CheckClasspathForProhibitedDependencies extends DefaultTask {
if (group.equals("javax.money")) { if (group.equals("javax.money")) {
return false; return false;
} }
if (group.equals("org.codehaus.groovy")) {
return true;
}
if (group.equals("org.eclipse.jetty.toolchain")) {
return true;
}
if (group.startsWith("javax")) { if (group.startsWith("javax")) {
return true; return true;
} }
@ -100,9 +94,6 @@ public class CheckClasspathForProhibitedDependencies extends DefaultTask {
if (group.equals("org.apache.geronimo.specs")) { if (group.equals("org.apache.geronimo.specs")) {
return true; return true;
} }
if (group.equals("com.sun.activation")) {
return true;
}
return false; return false;
} }

@ -88,8 +88,8 @@ public class CheckClasspathForUnnecessaryExclusions extends DefaultTask {
} }
private void processDependency(Dependency dependency) { private void processDependency(Dependency dependency) {
if (dependency instanceof ModuleDependency moduleDependency) { if (dependency instanceof ModuleDependency) {
processDependency(moduleDependency); processDependency((ModuleDependency) dependency);
} }
} }

@ -27,6 +27,7 @@ import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonMappingException;
@ -94,7 +95,7 @@ public class CheckAdditionalSpringConfigurationMetadata extends SourceTask {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void check(String key, Map<String, Object> json, Analysis analysis) { private void check(String key, Map<String, Object> json, Analysis analysis) {
List<Map<String, Object>> groups = (List<Map<String, Object>>) json.get(key); List<Map<String, Object>> groups = (List<Map<String, Object>>) json.get(key);
List<String> names = groups.stream().map((group) -> (String) group.get("name")).toList(); List<String> names = groups.stream().map((group) -> (String) group.get("name")).collect(Collectors.toList());
List<String> sortedNames = sortedCopy(names); List<String> sortedNames = sortedCopy(names);
for (int i = 0; i < names.size(); i++) { for (int i = 0; i < names.size(); i++) {
String actual = names.get(i); String actual = names.get(i);

@ -25,6 +25,7 @@ import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonMappingException;
@ -153,7 +154,9 @@ public class CheckSpringConfigurationMetadata extends DefaultTask {
else { else {
lines.add("The following properties have no description:"); lines.add("The following properties have no description:");
lines.add(""); lines.add("");
lines.addAll(this.propertiesWithNoDescription.stream().map((line) -> "\t" + line).toList()); lines.addAll(this.propertiesWithNoDescription.stream()
.map((line) -> "\t" + line)
.collect(Collectors.toList()));
} }
lines.add(""); lines.add("");
return lines.iterator(); return lines.iterator();

@ -33,7 +33,6 @@ import org.gradle.api.tasks.TaskProvider;
import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.language.base.plugins.LifecycleBasePlugin; import org.gradle.language.base.plugins.LifecycleBasePlugin;
import org.springframework.boot.build.processors.ProcessedAnnotationsPlugin;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -76,7 +75,7 @@ public class ConfigurationPropertiesPlugin implements Plugin<Project> {
@Override @Override
public void apply(Project project) { public void apply(Project project) {
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> { project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
configureConfigurationPropertiesAnnotationProcessor(project); addConfigurationProcessorDependency(project);
disableIncrementalCompilation(project); disableIncrementalCompilation(project);
configureAdditionalMetadataLocationsCompilerArgument(project); configureAdditionalMetadataLocationsCompilerArgument(project);
registerCheckAdditionalMetadataTask(project); registerCheckAdditionalMetadataTask(project);
@ -85,14 +84,13 @@ public class ConfigurationPropertiesPlugin implements Plugin<Project> {
}); });
} }
private void configureConfigurationPropertiesAnnotationProcessor(Project project) { private void addConfigurationProcessorDependency(Project project) {
Configuration annotationProcessors = project.getConfigurations() Configuration annotationProcessors = project.getConfigurations()
.getByName(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME); .getByName(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME);
annotationProcessors.getDependencies() annotationProcessors.getDependencies()
.add(project.getDependencies() .add(project.getDependencies()
.project(Collections.singletonMap("path", .project(Collections.singletonMap("path",
":spring-boot-project:spring-boot-tools:spring-boot-configuration-processor"))); ":spring-boot-project:spring-boot-tools:spring-boot-configuration-processor")));
project.getPlugins().apply(ProcessedAnnotationsPlugin.class);
} }
private void disableIncrementalCompilation(Project project) { private void disableIncrementalCompilation(Project project) {

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -78,7 +78,6 @@ public class DocumentConfigurationProperties extends DefaultTask {
snippets.add("application-properties.security", "Security Properties", this::securityPrefixes); snippets.add("application-properties.security", "Security Properties", this::securityPrefixes);
snippets.add("application-properties.rsocket", "RSocket Properties", this::rsocketPrefixes); snippets.add("application-properties.rsocket", "RSocket Properties", this::rsocketPrefixes);
snippets.add("application-properties.actuator", "Actuator Properties", this::actuatorPrefixes); snippets.add("application-properties.actuator", "Actuator Properties", this::actuatorPrefixes);
snippets.add("application-properties.docker-compose", "Docker Compose Properties", this::dockerComposePrefixes);
snippets.add("application-properties.devtools", "Devtools Properties", this::devtoolsPrefixes); snippets.add("application-properties.devtools", "Devtools Properties", this::devtoolsPrefixes);
snippets.add("application-properties.testing", "Testing Properties", this::testingPrefixes); snippets.add("application-properties.testing", "Testing Properties", this::testingPrefixes);
snippets.writeTo(this.outputDir.toPath()); snippets.writeTo(this.outputDir.toPath());
@ -104,9 +103,7 @@ public class DocumentConfigurationProperties extends DefaultTask {
config.accept("spring.profiles"); config.accept("spring.profiles");
config.accept("spring.quartz"); config.accept("spring.quartz");
config.accept("spring.reactor"); config.accept("spring.reactor");
config.accept("spring.ssl");
config.accept("spring.task"); config.accept("spring.task");
config.accept("spring.threads");
config.accept("spring.mandatory-file-encoding"); config.accept("spring.mandatory-file-encoding");
config.accept("info"); config.accept("info");
config.accept("spring.output.ansi.enabled"); config.accept("spring.output.ansi.enabled");
@ -128,13 +125,13 @@ public class DocumentConfigurationProperties extends DefaultTask {
private void dataPrefixes(Config config) { private void dataPrefixes(Config config) {
config.accept("spring.couchbase"); config.accept("spring.couchbase");
config.accept("spring.cassandra");
config.accept("spring.elasticsearch"); config.accept("spring.elasticsearch");
config.accept("spring.h2"); config.accept("spring.h2");
config.accept("spring.influx"); config.accept("spring.influx");
config.accept("spring.ldap"); config.accept("spring.ldap");
config.accept("spring.mongodb"); config.accept("spring.mongodb");
config.accept("spring.neo4j"); config.accept("spring.neo4j");
config.accept("spring.redis");
config.accept("spring.dao"); config.accept("spring.dao");
config.accept("spring.data"); config.accept("spring.data");
config.accept("spring.datasource"); config.accept("spring.datasource");
@ -171,7 +168,6 @@ public class DocumentConfigurationProperties extends DefaultTask {
prefix.accept("spring.integration"); prefix.accept("spring.integration");
prefix.accept("spring.jms"); prefix.accept("spring.jms");
prefix.accept("spring.kafka"); prefix.accept("spring.kafka");
prefix.accept("spring.pulsar");
prefix.accept("spring.rabbitmq"); prefix.accept("spring.rabbitmq");
prefix.accept("spring.hazelcast"); prefix.accept("spring.hazelcast");
prefix.accept("spring.webservices"); prefix.accept("spring.webservices");
@ -181,11 +177,11 @@ public class DocumentConfigurationProperties extends DefaultTask {
prefix.accept("spring.graphql"); prefix.accept("spring.graphql");
prefix.accept("spring.hateoas"); prefix.accept("spring.hateoas");
prefix.accept("spring.http"); prefix.accept("spring.http");
prefix.accept("spring.servlet");
prefix.accept("spring.jersey"); prefix.accept("spring.jersey");
prefix.accept("spring.mvc"); prefix.accept("spring.mvc");
prefix.accept("spring.netty"); prefix.accept("spring.netty");
prefix.accept("spring.resources"); prefix.accept("spring.resources");
prefix.accept("spring.servlet");
prefix.accept("spring.session"); prefix.accept("spring.session");
prefix.accept("spring.web"); prefix.accept("spring.web");
prefix.accept("spring.webflux"); prefix.accept("spring.webflux");
@ -215,10 +211,6 @@ public class DocumentConfigurationProperties extends DefaultTask {
prefix.accept("management"); prefix.accept("management");
} }
private void dockerComposePrefixes(Config prefix) {
prefix.accept("spring.docker.compose");
}
private void devtoolsPrefixes(Config prefix) { private void devtoolsPrefixes(Config prefix) {
prefix.accept("spring.devtools"); prefix.accept("spring.devtools");
} }

@ -21,6 +21,7 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -105,8 +106,8 @@ public class ApplicationRunner extends DefaultTask {
} }
public void normalizeTomcatPort() { public void normalizeTomcatPort() {
this.normalizations.put("(Tomcat started on port )[\\d]+( \\(http\\))", "$18080$2"); this.normalizations.put("(Tomcat started on port\\(s\\): )[\\d]+( \\(http\\))", "$18080$2");
this.normalizations.put("(Tomcat initialized with port )[\\d]+( \\(http\\))", "$18080$2"); this.normalizations.put("(Tomcat initialized with port\\(s\\): )[\\d]+( \\(http\\))", "$18080$2");
} }
public void normalizeLiveReloadPort() { public void normalizeLiveReloadPort() {
@ -138,8 +139,10 @@ public class ApplicationRunner extends DefaultTask {
private void awaitLogging(Process process) { private void awaitLogging(Process process) {
long end = System.currentTimeMillis() + 60000; long end = System.currentTimeMillis() + 60000;
String expectedLogging = this.expectedLogging.get(); String expectedLogging = this.expectedLogging.get();
List<String> outputLines = Collections.emptyList();
while (System.currentTimeMillis() < end) { while (System.currentTimeMillis() < end) {
for (String line : outputLines()) { outputLines = outputLines();
for (String line : outputLines) {
if (line.contains(expectedLogging)) { if (line.contains(expectedLogging)) {
return; return;
} }
@ -148,7 +151,10 @@ public class ApplicationRunner extends DefaultTask {
throw new IllegalStateException("Process exited before '" + expectedLogging + "' was logged"); throw new IllegalStateException("Process exited before '" + expectedLogging + "' was logged");
} }
} }
throw new IllegalStateException("'" + expectedLogging + "' was not logged within 60 seconds"); StringBuilder message = new StringBuilder(
"After 60 seconds '" + expectedLogging + "' had not be logged in the following output:\n\n");
outputLines.forEach((line) -> message.append(line).append("\n"));
throw new IllegalStateException(message.toString());
} }
private List<String> outputLines() { private List<String> outputLines() {
@ -176,8 +182,8 @@ public class ApplicationRunner extends DefaultTask {
private List<String> normalize(List<String> lines) { private List<String> normalize(List<String> lines) {
List<String> normalizedLines = lines; List<String> normalizedLines = lines;
Map<String, String> normalizations = new HashMap<>(this.normalizations); Map<String, String> normalizations = new HashMap<>(this.normalizations);
normalizations.put("(Starting .* using Java .* with PID [\\d]+ \\().*( started by ).*( in ).*(\\))", normalizations.put("(Starting .* using Java .* on ).*( with PID [\\d]+ \\().*( started by ).*( in ).*(\\))",
"$1" + this.applicationJar.get() + "$2myuser$3/opt/apps/$4"); "$1myhost$2" + this.applicationJar.get() + "$3myuser$4/opt/apps/$5");
for (Entry<String, String> normalization : normalizations.entrySet()) { for (Entry<String, String> normalization : normalizations.entrySet()) {
Pattern pattern = Pattern.compile(normalization.getKey()); Pattern pattern = Pattern.compile(normalization.getKey());
normalizedLines = normalize(normalizedLines, pattern, normalization.getValue()); normalizedLines = normalize(normalizedLines, pattern, normalization.getValue());
@ -190,7 +196,7 @@ public class ApplicationRunner extends DefaultTask {
List<String> normalizedLines = new ArrayList<>(); List<String> normalizedLines = new ArrayList<>();
for (String line : lines) { for (String line : lines) {
Matcher matcher = pattern.matcher(line); Matcher matcher = pattern.matcher(line);
StringBuilder transformed = new StringBuilder(); StringBuffer transformed = new StringBuffer();
while (matcher.find()) { while (matcher.find()) {
matched = true; matched = true;
matcher.appendReplacement(transformed, replacement); matcher.appendReplacement(transformed, replacement);

@ -22,6 +22,7 @@ import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import org.gradle.api.DefaultTask; import org.gradle.api.DefaultTask;
import org.gradle.api.Task; import org.gradle.api.Task;
@ -110,8 +111,13 @@ public class DocumentPluginGoals extends DefaultTask {
writer.printf("`%s:%s:%s`%n", plugin.getGroupId(), plugin.getArtifactId(), plugin.getVersion()); writer.printf("`%s:%s:%s`%n", plugin.getGroupId(), plugin.getArtifactId(), plugin.getVersion());
writer.println(); writer.println();
writer.println(mojo.getDescription()); writer.println(mojo.getDescription());
List<Parameter> parameters = mojo.getParameters().stream().filter(Parameter::isEditable).toList(); List<Parameter> parameters = mojo.getParameters()
List<Parameter> requiredParameters = parameters.stream().filter(Parameter::isRequired).toList(); .stream()
.filter(Parameter::isEditable)
.collect(Collectors.toList());
List<Parameter> requiredParameters = parameters.stream()
.filter(Parameter::isRequired)
.collect(Collectors.toList());
String detailsSectionId = sectionId + ".parameter-details"; String detailsSectionId = sectionId + ".parameter-details";
if (!requiredParameters.isEmpty()) { if (!requiredParameters.isEmpty()) {
writer.println(); writer.println();
@ -122,7 +128,7 @@ public class DocumentPluginGoals extends DefaultTask {
} }
List<Parameter> optionalParameters = parameters.stream() List<Parameter> optionalParameters = parameters.stream()
.filter((parameter) -> !parameter.isRequired()) .filter((parameter) -> !parameter.isRequired())
.toList(); .collect(Collectors.toList());
if (!optionalParameters.isEmpty()) { if (!optionalParameters.isEmpty()) {
writer.println(); writer.println();
writer.println(); writer.println();

@ -86,8 +86,8 @@ public class MavenExec extends JavaExec {
return existing; return existing;
} }
return project.getConfigurations().create("maven", (maven) -> { return project.getConfigurations().create("maven", (maven) -> {
maven.getDependencies().add(project.getDependencies().create("org.apache.maven:maven-embedder:3.6.3")); 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.3")); 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.slf4j:slf4j-simple:1.7.5"));
maven.getDependencies() maven.getDependencies()
.add(project.getDependencies() .add(project.getDependencies()

@ -38,6 +38,9 @@ import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathFactory;
import io.spring.javaformat.config.IndentationStyle;
import io.spring.javaformat.config.JavaBaseline;
import io.spring.javaformat.config.JavaFormatConfig;
import io.spring.javaformat.formatter.FileEdit; import io.spring.javaformat.formatter.FileEdit;
import io.spring.javaformat.formatter.FileFormatter; import io.spring.javaformat.formatter.FileFormatter;
import org.gradle.api.DefaultTask; import org.gradle.api.DefaultTask;
@ -99,6 +102,20 @@ import org.springframework.util.Assert;
*/ */
public class MavenPluginPlugin implements Plugin<Project> { public class MavenPluginPlugin implements Plugin<Project> {
private static final JavaFormatConfig FORMATTER_CONFIG = new JavaFormatConfig() {
@Override
public JavaBaseline getJavaBaseline() {
return JavaBaseline.V8;
}
@Override
public IndentationStyle getIndentationStyle() {
return IndentationStyle.TABS;
}
};
@Override @Override
public void apply(Project project) { public void apply(Project project) {
project.getPlugins().apply(JavaLibraryPlugin.class); project.getPlugins().apply(JavaLibraryPlugin.class);
@ -173,7 +190,7 @@ public class MavenPluginPlugin implements Plugin<Project> {
private MavenExec createGenerateHelpMojoTask(Project project, File helpMojoDir) { private MavenExec createGenerateHelpMojoTask(Project project, File helpMojoDir) {
MavenExec task = project.getTasks().create("generateHelpMojo", MavenExec.class); MavenExec task = project.getTasks().create("generateHelpMojo", MavenExec.class);
task.setProjectDir(helpMojoDir); task.setProjectDir(helpMojoDir);
task.args("org.apache.maven.plugins:maven-plugin-plugin:3.6.1:helpmojo"); task.args("org.apache.maven.plugins:maven-plugin-plugin:3.6.0:helpmojo");
task.getOutputs().dir(new File(helpMojoDir, "target/generated-sources/plugin")); task.getOutputs().dir(new File(helpMojoDir, "target/generated-sources/plugin"));
return task; return task;
} }
@ -240,7 +257,7 @@ public class MavenPluginPlugin implements Plugin<Project> {
private MavenExec createGeneratePluginDescriptorTask(Project project, File mavenDir) { private MavenExec createGeneratePluginDescriptorTask(Project project, File mavenDir) {
MavenExec generatePluginDescriptor = project.getTasks().create("generatePluginDescriptor", MavenExec.class); MavenExec generatePluginDescriptor = project.getTasks().create("generatePluginDescriptor", MavenExec.class);
generatePluginDescriptor.args("org.apache.maven.plugins:maven-plugin-plugin:3.6.1:descriptor"); generatePluginDescriptor.args("org.apache.maven.plugins:maven-plugin-plugin:3.6.0:descriptor");
generatePluginDescriptor.getOutputs().dir(new File(mavenDir, "target/classes/META-INF/maven")); generatePluginDescriptor.getOutputs().dir(new File(mavenDir, "target/classes/META-INF/maven"));
generatePluginDescriptor.getInputs() generatePluginDescriptor.getInputs()
.dir(new File(mavenDir, "target/classes/org")) .dir(new File(mavenDir, "target/classes/org"))
@ -310,7 +327,7 @@ public class MavenPluginPlugin implements Plugin<Project> {
@TaskAction @TaskAction
void syncAndFormat() { void syncAndFormat() {
FileFormatter formatter = new FileFormatter(); FileFormatter formatter = new FileFormatter(FORMATTER_CONFIG);
for (File output : this.generator.getOutputs().getFiles()) { for (File output : this.generator.getOutputs().getFiles()) {
formatter.formatFiles(getProject().fileTree(output), StandardCharsets.UTF_8) formatter.formatFiles(getProject().fileTree(output), StandardCharsets.UTF_8)
.forEach((edit) -> save(output, edit)); .forEach((edit) -> save(output, edit));
@ -322,7 +339,7 @@ public class MavenPluginPlugin implements Plugin<Project> {
Path outputLocation = this.outputDir.toPath().resolve(relativePath); Path outputLocation = this.outputDir.toPath().resolve(relativePath);
try { try {
Files.createDirectories(outputLocation.getParent()); Files.createDirectories(outputLocation.getParent());
Files.writeString(outputLocation, edit.getFormattedContent()); Files.write(outputLocation, edit.getFormattedContent().getBytes(StandardCharsets.UTF_8));
} }
catch (Exception ex) { catch (Exception ex) {
throw new TaskExecutionException(this, ex); throw new TaskExecutionException(this, ex);
@ -387,7 +404,9 @@ public class MavenPluginPlugin implements Plugin<Project> {
@TaskAction @TaskAction
public void createRepository() { public void createRepository() {
for (ResolvedArtifactResult result : this.runtimeClasspath.getIncoming().getArtifacts()) { for (ResolvedArtifactResult result : this.runtimeClasspath.getIncoming().getArtifacts()) {
if (result.getId().getComponentIdentifier() instanceof ModuleComponentIdentifier identifier) { if (result.getId().getComponentIdentifier() instanceof ModuleComponentIdentifier) {
ModuleComponentIdentifier identifier = (ModuleComponentIdentifier) result.getId()
.getComponentIdentifier();
String fileName = result.getFile() String fileName = result.getFile()
.getName() .getName()
.replace(identifier.getVersion() + "-" + identifier.getVersion(), identifier.getVersion()); .replace(identifier.getVersion() + "-" + identifier.getVersion(), identifier.getVersion());
@ -463,8 +482,6 @@ public class MavenPluginPlugin implements Plugin<Project> {
effectiveBom.property("spring-framework.version", versions::setProperty); effectiveBom.property("spring-framework.version", versions::setProperty);
effectiveBom.property("jakarta-servlet.version", versions::setProperty); effectiveBom.property("jakarta-servlet.version", versions::setProperty);
effectiveBom.property("kotlin.version", versions::setProperty); effectiveBom.property("kotlin.version", versions::setProperty);
effectiveBom.property("assertj.version", versions::setProperty);
effectiveBom.property("junit-jupiter.version", versions::setProperty);
return versions; return versions;
} }

@ -139,7 +139,7 @@ class PluginXmlParser {
@Override @Override
public Iterator<Node> iterator() { public Iterator<Node> iterator() {
return new Iterator<>() { return new Iterator<Node>() {
private int index = 0; private int index = 0;

@ -35,7 +35,7 @@ import org.gradle.api.tasks.TaskAction;
*/ */
public class PrepareMavenBinaries extends DefaultTask { public class PrepareMavenBinaries extends DefaultTask {
private final Set<String> versions = new LinkedHashSet<>(); private Set<String> versions = new LinkedHashSet<>();
private File outputDir; private File outputDir;

@ -1,39 +0,0 @@
/*
* Copyright 2022-2023 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.processors;
import com.diffplug.gradle.eclipse.apt.AptEclipsePlugin;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.plugins.ide.eclipse.model.EclipseModel;
/**
* A plugin for a project that uses one or more annotations processors.
*
* @author Andy Wilkinson
*/
public class ProcessedAnnotationsPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getPlugins().apply(AptEclipsePlugin.class);
project.getExtensions()
.getByType(EclipseModel.class)
.synchronizationTasks("eclipseJdtApt", "eclipseJdt", "eclipseFactorypath");
}
}

@ -16,12 +16,11 @@
package org.springframework.boot.build.testing; package org.springframework.boot.build.testing;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.stream.Collectors;
import org.gradle.api.DefaultTask;
import org.gradle.api.services.BuildService; import org.gradle.api.services.BuildService;
import org.gradle.api.services.BuildServiceParameters; import org.gradle.api.services.BuildServiceParameters;
import org.gradle.api.tasks.testing.Test; import org.gradle.api.tasks.testing.Test;
@ -37,12 +36,16 @@ import org.gradle.tooling.events.OperationCompletionListener;
public abstract class TestResultsOverview public abstract class TestResultsOverview
implements BuildService<BuildServiceParameters.None>, OperationCompletionListener, AutoCloseable { implements BuildService<BuildServiceParameters.None>, OperationCompletionListener, AutoCloseable {
private final Map<Test, List<TestFailure>> testFailures = new TreeMap<>(Comparator.comparing(DefaultTask::getPath)); private final Map<Test, List<TestFailure>> testFailures = new TreeMap<>(
(one, two) -> one.getPath().compareTo(two.getPath()));
private final Object monitor = new Object(); private final Object monitor = new Object();
void addFailures(Test test, List<TestDescriptor> failureDescriptors) { void addFailures(Test test, List<TestDescriptor> failureDescriptors) {
List<TestFailure> testFailures = failureDescriptors.stream().map(TestFailure::new).sorted().toList(); List<TestFailure> testFailures = failureDescriptors.stream()
.map(TestFailure::new)
.sorted()
.collect(Collectors.toList());
synchronized (this.monitor) { synchronized (this.monitor) {
this.testFailures.put(test, testFailures); this.testFailures.put(test, testFailures);
} }

@ -76,7 +76,7 @@ class ConventionsPluginTests {
out.println(" id 'org.springframework.boot.conventions'"); out.println(" id 'org.springframework.boot.conventions'");
out.println("}"); out.println("}");
out.println("version = '1.2.3'"); out.println("version = '1.2.3'");
out.println("sourceCompatibility = '17'"); out.println("sourceCompatibility = '1.8'");
out.println("description 'Test project for manifest customization'"); out.println("description 'Test project for manifest customization'");
out.println("jar.archiveFileName = 'test.jar'"); out.println("jar.archiveFileName = 'test.jar'");
} }
@ -93,7 +93,7 @@ class ConventionsPluginTests {
.isEqualTo(this.projectDir.getName().replace("-", ".")); .isEqualTo(this.projectDir.getName().replace("-", "."));
assertThat(mainAttributes.getValue("Implementation-Version")).isEqualTo("1.2.3"); assertThat(mainAttributes.getValue("Implementation-Version")).isEqualTo("1.2.3");
assertThat(mainAttributes.getValue("Built-By")).isEqualTo("Spring"); assertThat(mainAttributes.getValue("Built-By")).isEqualTo("Spring");
assertThat(mainAttributes.getValue("Build-Jdk-Spec")).isEqualTo("17"); assertThat(mainAttributes.getValue("Build-Jdk-Spec")).isEqualTo("1.8");
} }
} }
@ -106,7 +106,7 @@ class ConventionsPluginTests {
out.println(" id 'org.springframework.boot.conventions'"); out.println(" id 'org.springframework.boot.conventions'");
out.println("}"); out.println("}");
out.println("version = '1.2.3'"); out.println("version = '1.2.3'");
out.println("sourceCompatibility = '17'"); out.println("sourceCompatibility = '1.8'");
out.println("description 'Test'"); out.println("description 'Test'");
} }
runGradle("assemble"); runGradle("assemble");
@ -122,7 +122,7 @@ class ConventionsPluginTests {
.isEqualTo(this.projectDir.getName().replace("-", ".")); .isEqualTo(this.projectDir.getName().replace("-", "."));
assertThat(mainAttributes.getValue("Implementation-Version")).isEqualTo("1.2.3"); assertThat(mainAttributes.getValue("Implementation-Version")).isEqualTo("1.2.3");
assertThat(mainAttributes.getValue("Built-By")).isEqualTo("Spring"); assertThat(mainAttributes.getValue("Built-By")).isEqualTo("Spring");
assertThat(mainAttributes.getValue("Build-Jdk-Spec")).isEqualTo("17"); assertThat(mainAttributes.getValue("Build-Jdk-Spec")).isEqualTo("1.8");
} }
} }
@ -135,7 +135,7 @@ class ConventionsPluginTests {
out.println(" id 'org.springframework.boot.conventions'"); out.println(" id 'org.springframework.boot.conventions'");
out.println("}"); out.println("}");
out.println("version = '1.2.3'"); out.println("version = '1.2.3'");
out.println("sourceCompatibility = '17'"); out.println("sourceCompatibility = '1.8'");
out.println("description 'Test'"); out.println("description 'Test'");
} }
runGradle("assemble"); runGradle("assemble");
@ -151,7 +151,7 @@ class ConventionsPluginTests {
.isEqualTo(this.projectDir.getName().replace("-", ".")); .isEqualTo(this.projectDir.getName().replace("-", "."));
assertThat(mainAttributes.getValue("Implementation-Version")).isEqualTo("1.2.3"); assertThat(mainAttributes.getValue("Implementation-Version")).isEqualTo("1.2.3");
assertThat(mainAttributes.getValue("Built-By")).isEqualTo("Spring"); assertThat(mainAttributes.getValue("Built-By")).isEqualTo("Spring");
assertThat(mainAttributes.getValue("Build-Jdk-Spec")).isEqualTo("17"); assertThat(mainAttributes.getValue("Build-Jdk-Spec")).isEqualTo("1.8");
} }
} }

@ -45,7 +45,7 @@ class ArchitectureCheckTests {
@Test @Test
void whenPackagesAreTangledTaskFailsAndWritesAReport() throws Exception { void whenPackagesAreTangledTaskFailsAndWritesAReport() throws Exception {
prepareTask("tangled", (architectureCheck) -> { prepareTask("tangled", (architectureCheck) -> {
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture); assertThatExceptionOfType(GradleException.class).isThrownBy(() -> architectureCheck.checkArchitecture());
assertThat(failureReport(architectureCheck).length()).isGreaterThan(0); assertThat(failureReport(architectureCheck).length()).isGreaterThan(0);
}); });
} }
@ -65,7 +65,7 @@ class ArchitectureCheckTests {
@Test @Test
void whenBeanPostProcessorBeanMethodIsNotStaticTaskFailsAndWritesAReport() throws Exception { void whenBeanPostProcessorBeanMethodIsNotStaticTaskFailsAndWritesAReport() throws Exception {
prepareTask("bpp/nonstatic", (architectureCheck) -> { prepareTask("bpp/nonstatic", (architectureCheck) -> {
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture); assertThatExceptionOfType(GradleException.class).isThrownBy(() -> architectureCheck.checkArchitecture());
assertThat(failureReport(architectureCheck).length()).isGreaterThan(0); assertThat(failureReport(architectureCheck).length()).isGreaterThan(0);
}); });
} }
@ -73,7 +73,7 @@ class ArchitectureCheckTests {
@Test @Test
void whenBeanPostProcessorBeanMethodIsStaticAndHasUnsafeParametersTaskFailsAndWritesAReport() throws Exception { void whenBeanPostProcessorBeanMethodIsStaticAndHasUnsafeParametersTaskFailsAndWritesAReport() throws Exception {
prepareTask("bpp/unsafeparameters", (architectureCheck) -> { prepareTask("bpp/unsafeparameters", (architectureCheck) -> {
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture); assertThatExceptionOfType(GradleException.class).isThrownBy(() -> architectureCheck.checkArchitecture());
assertThat(failureReport(architectureCheck).length()).isGreaterThan(0); assertThat(failureReport(architectureCheck).length()).isGreaterThan(0);
}); });
} }
@ -99,7 +99,7 @@ class ArchitectureCheckTests {
@Test @Test
void whenBeanFactoryPostProcessorBeanMethodIsNotStaticTaskFailsAndWritesAReport() throws Exception { void whenBeanFactoryPostProcessorBeanMethodIsNotStaticTaskFailsAndWritesAReport() throws Exception {
prepareTask("bfpp/nonstatic", (architectureCheck) -> { prepareTask("bfpp/nonstatic", (architectureCheck) -> {
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture); assertThatExceptionOfType(GradleException.class).isThrownBy(() -> architectureCheck.checkArchitecture());
assertThat(failureReport(architectureCheck).length()).isGreaterThan(0); assertThat(failureReport(architectureCheck).length()).isGreaterThan(0);
}); });
} }
@ -107,7 +107,7 @@ class ArchitectureCheckTests {
@Test @Test
void whenBeanFactoryPostProcessorBeanMethodIsStaticAndHasParametersTaskFailsAndWritesAReport() throws Exception { void whenBeanFactoryPostProcessorBeanMethodIsStaticAndHasParametersTaskFailsAndWritesAReport() throws Exception {
prepareTask("bfpp/parameters", (architectureCheck) -> { prepareTask("bfpp/parameters", (architectureCheck) -> {
assertThatExceptionOfType(GradleException.class).isThrownBy(architectureCheck::checkArchitecture); assertThatExceptionOfType(GradleException.class).isThrownBy(() -> architectureCheck.checkArchitecture());
assertThat(failureReport(architectureCheck).length()).isGreaterThan(0); assertThat(failureReport(architectureCheck).length()).isGreaterThan(0);
}); });
} }

@ -20,6 +20,7 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.Properties; import java.util.Properties;
@ -48,14 +49,14 @@ class UpgradeApplicatorTests {
void whenUpgradeIsAppliedToLibraryWithVersionThenBomIsUpdated() throws IOException { void whenUpgradeIsAppliedToLibraryWithVersionThenBomIsUpdated() throws IOException {
File bom = new File(this.temp, "bom.gradle"); File bom = new File(this.temp, "bom.gradle");
FileCopyUtils.copy(new File("src/test/resources/bom.gradle"), bom); FileCopyUtils.copy(new File("src/test/resources/bom.gradle"), bom);
String originalContents = Files.readString(bom.toPath()); String originalContents = new String(Files.readAllBytes(bom.toPath()), StandardCharsets.UTF_8);
File gradleProperties = new File(this.temp, "gradle.properties"); File gradleProperties = new File(this.temp, "gradle.properties");
FileCopyUtils.copy(new File("src/test/resources/gradle.properties"), gradleProperties); FileCopyUtils.copy(new File("src/test/resources/gradle.properties"), gradleProperties);
new UpgradeApplicator(bom.toPath(), gradleProperties.toPath()) new UpgradeApplicator(bom.toPath(), gradleProperties.toPath())
.apply(new Upgrade(new Library("ActiveMQ", null, new LibraryVersion(DependencyVersion.parse("5.15.11")), .apply(new Upgrade(new Library("ActiveMQ", null, new LibraryVersion(DependencyVersion.parse("5.15.11")),
null, null, false), DependencyVersion.parse("5.16"))); null, null, false), DependencyVersion.parse("5.16")));
String bomContents = Files.readString(bom.toPath()); String bomContents = new String(Files.readAllBytes(bom.toPath()), StandardCharsets.UTF_8);
assertThat(bomContents).hasSize(originalContents.length() - 3); assertThat(bomContents.length()).isEqualTo(originalContents.length() - 3);
} }
@Test @Test

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -39,8 +39,8 @@ class CompoundRowTests {
row.addProperty(new ConfigurationProperty("spring.test.third", "java.lang.String")); row.addProperty(new ConfigurationProperty("spring.test.third", "java.lang.String"));
Asciidoc asciidoc = new Asciidoc(); Asciidoc asciidoc = new Asciidoc();
row.write(asciidoc); row.write(asciidoc);
assertThat(asciidoc).hasToString("|[[my.spring.test]]<<my.spring.test,`+spring.test.first+` +" + NEWLINE assertThat(asciidoc.toString()).isEqualTo("|[[my.spring.test]]<<my.spring.test,`+spring.test.first+` +"
+ "`+spring.test.second+` +" + NEWLINE + "`+spring.test.third+` +" + NEWLINE + ">>" + NEWLINE + NEWLINE + "`+spring.test.second+` +" + NEWLINE + "`+spring.test.third+` +" + NEWLINE + ">>" + NEWLINE
+ "|+++This is a description.+++" + NEWLINE + "|" + NEWLINE); + "|+++This is a description.+++" + NEWLINE + "|" + NEWLINE);
} }

@ -38,7 +38,7 @@ class SingleRowTests {
SingleRow row = new SingleRow(SNIPPET, property); SingleRow row = new SingleRow(SNIPPET, property);
Asciidoc asciidoc = new Asciidoc(); Asciidoc asciidoc = new Asciidoc();
row.write(asciidoc); row.write(asciidoc);
assertThat(asciidoc).hasToString("|[[my.spring.test.prop]]<<my.spring.test.prop,`+spring.test.prop+`>>" assertThat(asciidoc.toString()).isEqualTo("|[[my.spring.test.prop]]<<my.spring.test.prop,`+spring.test.prop+`>>"
+ NEWLINE + "|+++This is a description.+++" + NEWLINE + "|`+something+`" + NEWLINE); + NEWLINE + "|+++This is a description.+++" + NEWLINE + "|`+something+`" + NEWLINE);
} }
@ -49,7 +49,7 @@ class SingleRowTests {
SingleRow row = new SingleRow(SNIPPET, property); SingleRow row = new SingleRow(SNIPPET, property);
Asciidoc asciidoc = new Asciidoc(); Asciidoc asciidoc = new Asciidoc();
row.write(asciidoc); row.write(asciidoc);
assertThat(asciidoc).hasToString("|[[my.spring.test.prop]]<<my.spring.test.prop,`+spring.test.prop+`>>" assertThat(asciidoc.toString()).isEqualTo("|[[my.spring.test.prop]]<<my.spring.test.prop,`+spring.test.prop+`>>"
+ NEWLINE + "|+++This is a description.+++" + NEWLINE + "|" + NEWLINE); + NEWLINE + "|+++This is a description.+++" + NEWLINE + "|" + NEWLINE);
} }
@ -60,7 +60,7 @@ class SingleRowTests {
SingleRow row = new SingleRow(SNIPPET, property); SingleRow row = new SingleRow(SNIPPET, property);
Asciidoc asciidoc = new Asciidoc(); Asciidoc asciidoc = new Asciidoc();
row.write(asciidoc); row.write(asciidoc);
assertThat(asciidoc).hasToString("|[[my.spring.test.prop]]<<my.spring.test.prop,`+spring.test.prop+`>>" assertThat(asciidoc.toString()).isEqualTo("|[[my.spring.test.prop]]<<my.spring.test.prop,`+spring.test.prop+`>>"
+ NEWLINE + "|+++This is a description.+++" + NEWLINE + "|`+first\\|second+`" + NEWLINE); + NEWLINE + "|+++This is a description.+++" + NEWLINE + "|`+first\\|second+`" + NEWLINE);
} }
@ -71,7 +71,7 @@ class SingleRowTests {
SingleRow row = new SingleRow(SNIPPET, property); SingleRow row = new SingleRow(SNIPPET, property);
Asciidoc asciidoc = new Asciidoc(); Asciidoc asciidoc = new Asciidoc();
row.write(asciidoc); row.write(asciidoc);
assertThat(asciidoc).hasToString("|[[my.spring.test.prop]]<<my.spring.test.prop,`+spring.test.prop+`>>" assertThat(asciidoc.toString()).isEqualTo("|[[my.spring.test.prop]]<<my.spring.test.prop,`+spring.test.prop+`>>"
+ NEWLINE + "|+++This is a description.+++" + NEWLINE + "|`+first\\\\second+`" + NEWLINE); + NEWLINE + "|+++This is a description.+++" + NEWLINE + "|`+first\\\\second+`" + NEWLINE);
} }
@ -82,7 +82,7 @@ class SingleRowTests {
SingleRow row = new SingleRow(SNIPPET, property); SingleRow row = new SingleRow(SNIPPET, property);
Asciidoc asciidoc = new Asciidoc(); Asciidoc asciidoc = new Asciidoc();
row.write(asciidoc); row.write(asciidoc);
assertThat(asciidoc).hasToString("|[[my.spring.test.prop]]<<my.spring.test.prop,`+spring.test.prop+`>>" assertThat(asciidoc.toString()).isEqualTo("|[[my.spring.test.prop]]<<my.spring.test.prop,`+spring.test.prop+`>>"
+ NEWLINE + "|+++This is a description with a \\| pipe.+++" + NEWLINE + "|" + NEWLINE); + NEWLINE + "|+++This is a description with a \\| pipe.+++" + NEWLINE + "|" + NEWLINE);
} }
@ -93,8 +93,9 @@ class SingleRowTests {
SingleRow row = new SingleRow(SNIPPET, property); SingleRow row = new SingleRow(SNIPPET, property);
Asciidoc asciidoc = new Asciidoc(); Asciidoc asciidoc = new Asciidoc();
row.write(asciidoc); row.write(asciidoc);
assertThat(asciidoc).hasToString("|[[my.spring.test.prop]]<<my.spring.test.prop,`+spring.test.prop.*+`>>" assertThat(asciidoc.toString())
+ NEWLINE + "|+++This is a description.+++" + NEWLINE + "|" + NEWLINE); .isEqualTo("|[[my.spring.test.prop]]<<my.spring.test.prop,`+spring.test.prop.*+`>>" + NEWLINE
+ "|+++This is a description.+++" + NEWLINE + "|" + NEWLINE);
} }
@Test @Test
@ -105,7 +106,7 @@ class SingleRowTests {
SingleRow row = new SingleRow(SNIPPET, property); SingleRow row = new SingleRow(SNIPPET, property);
Asciidoc asciidoc = new Asciidoc(); Asciidoc asciidoc = new Asciidoc();
row.write(asciidoc); row.write(asciidoc);
assertThat(asciidoc).hasToString("|[[my.spring.test.prop]]<<my.spring.test.prop,`+spring.test.prop+`>>" assertThat(asciidoc.toString()).isEqualTo("|[[my.spring.test.prop]]<<my.spring.test.prop,`+spring.test.prop+`>>"
+ NEWLINE + "|+++This is a description.+++" + NEWLINE + "|`+first," + NEWLINE + "second," + NEWLINE + NEWLINE + "|+++This is a description.+++" + NEWLINE + "|`+first," + NEWLINE + "second," + NEWLINE
+ "third+`" + NEWLINE); + "third+`" + NEWLINE);
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -41,7 +41,8 @@ class TableTests {
Asciidoc asciidoc = new Asciidoc(); Asciidoc asciidoc = new Asciidoc();
table.write(asciidoc); table.write(asciidoc);
// @formatter:off // @formatter:off
assertThat(asciidoc).hasToString("[cols=\"4,3,3\", options=\"header\"]" + NEWLINE + assertThat(asciidoc.toString()).isEqualTo(
"[cols=\"4,3,3\", options=\"header\"]" + NEWLINE +
"|===" + NEWLINE + "|===" + NEWLINE +
"|Name|Description|Default Value" + NEWLINE + NEWLINE + "|Name|Description|Default Value" + NEWLINE + NEWLINE +
"|[[my.spring.test.other]]<<my.spring.test.other,`+spring.test.other+`>>" + NEWLINE + "|[[my.spring.test.other]]<<my.spring.test.other,`+spring.test.other+`>>" + NEWLINE +

@ -18,6 +18,7 @@ package org.springframework.boot.build.mavenplugin;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -43,8 +44,8 @@ class PluginXmlParserTests {
assertThat(plugin.getArtifactId()).isEqualTo("spring-boot-maven-plugin"); assertThat(plugin.getArtifactId()).isEqualTo("spring-boot-maven-plugin");
assertThat(plugin.getVersion()).isEqualTo("2.2.0.GRADLE-SNAPSHOT"); assertThat(plugin.getVersion()).isEqualTo("2.2.0.GRADLE-SNAPSHOT");
assertThat(plugin.getGoalPrefix()).isEqualTo("spring-boot"); assertThat(plugin.getGoalPrefix()).isEqualTo("spring-boot");
assertThat(plugin.getMojos().stream().map(PluginXmlParser.Mojo::getGoal)).containsExactly("build-info", "help", assertThat(plugin.getMojos().stream().map(PluginXmlParser.Mojo::getGoal).collect(Collectors.toList()))
"repackage", "run", "start", "stop"); .containsExactly("build-info", "help", "repackage", "run", "start", "stop");
} }
@Test @Test

@ -24,6 +24,7 @@ import java.io.PrintWriter;
import java.io.StringReader; import java.io.StringReader;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.GradleRunner; import org.gradle.testkit.runner.GradleRunner;
@ -185,7 +186,7 @@ class TestFailuresPluginIntegrationTests {
private List<String> readLines(String output) { private List<String> readLines(String output) {
try (BufferedReader reader = new BufferedReader(new StringReader(output))) { try (BufferedReader reader = new BufferedReader(new StringReader(output))) {
return reader.lines().toList(); return reader.lines().collect(Collectors.toList());
} }
catch (IOException ex) { catch (IOException ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);

@ -11,7 +11,7 @@ The pipeline can be deployed using the following command:
[source] [source]
---- ----
$ fly -t spring-boot set-pipeline -p spring-boot-3.2.x -c ci/pipeline.yml -l ci/parameters.yml $ fly -t spring-boot set-pipeline -p spring-boot-2.7.x -c ci/pipeline.yml -l ci/parameters.yml
---- ----
NOTE: This assumes that you have credhub integration configured with the appropriate NOTE: This assumes that you have credhub integration configured with the appropriate

@ -0,0 +1,10 @@
FROM ubuntu:jammy-20230916
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
ADD get-docker-url.sh /get-docker-url.sh
RUN ./setup.sh java11
ENV JAVA_HOME /opt/openjdk
ENV PATH $JAVA_HOME/bin:$PATH
ADD docker-lib.sh /docker-lib.sh

@ -0,0 +1,10 @@
FROM ubuntu:jammy-20230916
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
ADD get-docker-url.sh /get-docker-url.sh
RUN ./setup.sh java8 java17
ENV JAVA_HOME /opt/openjdk
ENV PATH $JAVA_HOME/bin:$PATH
ADD docker-lib.sh /docker-lib.sh

@ -3,12 +3,8 @@ FROM ubuntu:jammy-20230916
ADD setup.sh /setup.sh ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh ADD get-jdk-url.sh /get-jdk-url.sh
ADD get-docker-url.sh /get-docker-url.sh ADD get-docker-url.sh /get-docker-url.sh
ADD get-docker-compose-url.sh /get-docker-compose-url.sh RUN ./setup.sh java8 java21
RUN ./setup.sh java17 java21
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
ENV JAVA_HOME /opt/openjdk ENV JAVA_HOME /opt/openjdk
ENV PATH $JAVA_HOME/bin:$PATH ENV PATH $JAVA_HOME/bin:$PATH
ADD docker-lib.sh /docker-lib.sh ADD docker-lib.sh /docker-lib.sh

@ -3,12 +3,8 @@ FROM ubuntu:jammy-20230916
ADD setup.sh /setup.sh ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh ADD get-jdk-url.sh /get-jdk-url.sh
ADD get-docker-url.sh /get-docker-url.sh ADD get-docker-url.sh /get-docker-url.sh
ADD get-docker-compose-url.sh /get-docker-compose-url.sh RUN ./setup.sh java8
RUN ./setup.sh java17
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
ENV JAVA_HOME /opt/openjdk ENV JAVA_HOME /opt/openjdk
ENV PATH $JAVA_HOME/bin:$PATH ENV PATH $JAVA_HOME/bin:$PATH
ADD docker-lib.sh /docker-lib.sh ADD docker-lib.sh /docker-lib.sh

@ -1,5 +0,0 @@
#!/bin/bash
set -e
version="2.17.0"
echo "https://github.com/docker/compose/releases/download/v$version/docker-compose-linux-x86_64"

@ -2,6 +2,12 @@
set -e set -e
case "$1" in case "$1" in
java8)
echo "https://github.com/bell-sw/Liberica/releases/download/8u382+6/bellsoft-jdk8u382+6-linux-amd64.tar.gz"
;;
java11)
echo "https://github.com/bell-sw/Liberica/releases/download/11.0.20.1+1/bellsoft-jdk11.0.20.1+1-linux-amd64.tar.gz"
;;
java17) java17)
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.8.1+1/bellsoft-jdk17.0.8.1+1-linux-amd64.tar.gz" echo "https://github.com/bell-sw/Liberica/releases/download/17.0.8.1+1/bellsoft-jdk17.0.8.1+1-linux-amd64.tar.gz"
;; ;;

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -50,7 +50,7 @@ class ArtifactCollector {
Collection<DeployableArtifact> collectArtifacts(Path root) { Collection<DeployableArtifact> collectArtifacts(Path root) {
try (Stream<Path> artifacts = Files.walk(root)) { try (Stream<Path> artifacts = Files.walk(root)) {
return artifacts.filter(Files::isRegularFile).filter(this.excludeFilter) return artifacts.filter(Files::isRegularFile).filter(this.excludeFilter)
.map((artifact) -> deployableArtifact(artifact, root)).toList(); .map((artifact) -> deployableArtifact(artifact, root)).collect(Collectors.toList());
} }
catch (IOException ex) { catch (IOException ex) {
throw new RuntimeException("Could not read artifacts from '" + root + "'"); throw new RuntimeException("Could not read artifacts from '" + root + "'");

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -208,7 +208,7 @@ public class SonatypeService {
List<String> failureMessages = Stream.of(activities).flatMap((activity) -> activity.events.stream()) List<String> failureMessages = Stream.of(activities).flatMap((activity) -> activity.events.stream())
.filter((event) -> event.severity > 0).flatMap((event) -> event.properties.stream()) .filter((event) -> event.severity > 0).flatMap((event) -> event.properties.stream())
.filter((property) -> "failureMessage".equals(property.name)) .filter((property) -> "failureMessage".equals(property.name))
.map((property) -> " " + property.value).toList(); .map((property) -> " " + property.value).collect(Collectors.toList());
if (failureMessages.isEmpty()) { if (failureMessages.isEmpty()) {
logger.error("Close failed for unknown reasons"); logger.error("Close failed for unknown reasons");
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,8 +16,6 @@
package io.spring.concourse.releasescripts.artifactory; package io.spring.concourse.releasescripts.artifactory;
import java.util.Base64;
import io.spring.concourse.releasescripts.ReleaseInfo; import io.spring.concourse.releasescripts.ReleaseInfo;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -31,6 +29,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.test.web.client.response.DefaultResponseCreator; import org.springframework.test.web.client.response.DefaultResponseCreator;
import org.springframework.util.Base64Utils;
import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpClientErrorException;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@ -70,11 +69,8 @@ class ArtifactoryServiceTests {
.andExpect(method(HttpMethod.POST)) .andExpect(method(HttpMethod.POST))
.andExpect(content().json( .andExpect(content().json(
"{\"status\": \"staged\", \"sourceRepo\": \"libs-staging-local\", \"targetRepo\": \"libs-milestone-local\"}")) "{\"status\": \"staged\", \"sourceRepo\": \"libs-staging-local\", \"targetRepo\": \"libs-milestone-local\"}"))
.andExpect( .andExpect(header("Authorization", "Basic " + Base64Utils.encodeToString(String
header("Authorization", .format("%s:%s", this.properties.getUsername(), this.properties.getPassword()).getBytes())))
"Basic " + Base64.getEncoder()
.encodeToString(String.format("%s:%s", this.properties.getUsername(),
this.properties.getPassword()).getBytes())))
.andExpect(header("Content-Type", MediaType.APPLICATION_JSON.toString())).andRespond(withSuccess()); .andExpect(header("Content-Type", MediaType.APPLICATION_JSON.toString())).andRespond(withSuccess());
this.service.promote("libs-milestone-local", getReleaseInfo()); this.service.promote("libs-milestone-local", getReleaseInfo());
this.server.verify(); this.server.verify();

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,6 +21,7 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -104,7 +105,7 @@ class SonatypeServiceTests {
.filter((artifact) -> !artifact.startsWith("build-info.json")) .filter((artifact) -> !artifact.startsWith("build-info.json"))
.map((artifact) -> requestTo( .map((artifact) -> requestTo(
"/service/local/staging/deployByRepositoryId/example-6789/" + artifact.toString())) "/service/local/staging/deployByRepositoryId/example-6789/" + artifact.toString()))
.collect(Collectors.toSet()); .collect(Collectors.toCollection(HashSet::new));
AnyOfRequestMatcher uploadRequestsMatcher = anyOf(uploads); AnyOfRequestMatcher uploadRequestsMatcher = anyOf(uploads);
assertThat(uploadRequestsMatcher.candidates).hasSize(150); assertThat(uploadRequestsMatcher.candidates).hasSize(150);
this.server.expect(ExpectedCount.times(150), uploadRequestsMatcher).andExpect(method(HttpMethod.PUT)) this.server.expect(ExpectedCount.times(150), uploadRequestsMatcher).andExpect(method(HttpMethod.PUT))
@ -132,7 +133,7 @@ class SonatypeServiceTests {
.andRespond(withSuccess()); .andRespond(withSuccess());
this.service.publish(getReleaseInfo(), artifactsRoot); this.service.publish(getReleaseInfo(), artifactsRoot);
this.server.verify(); this.server.verify();
assertThat(uploadRequestsMatcher.candidates).isEmpty(); assertThat(uploadRequestsMatcher.candidates).hasSize(0);
} }
} }
@ -156,7 +157,7 @@ class SonatypeServiceTests {
.filter((artifact) -> !"build-info.json".equals(artifact.toString())) .filter((artifact) -> !"build-info.json".equals(artifact.toString()))
.map((artifact) -> requestTo( .map((artifact) -> requestTo(
"/service/local/staging/deployByRepositoryId/example-6789/" + artifact.toString())) "/service/local/staging/deployByRepositoryId/example-6789/" + artifact.toString()))
.collect(Collectors.toSet()); .collect(Collectors.toCollection(HashSet::new));
AnyOfRequestMatcher uploadRequestsMatcher = anyOf(uploads); AnyOfRequestMatcher uploadRequestsMatcher = anyOf(uploads);
assertThat(uploadRequestsMatcher.candidates).hasSize(150); assertThat(uploadRequestsMatcher.candidates).hasSize(150);
this.server.expect(ExpectedCount.times(150), uploadRequestsMatcher).andExpect(method(HttpMethod.PUT)) this.server.expect(ExpectedCount.times(150), uploadRequestsMatcher).andExpect(method(HttpMethod.PUT))
@ -184,7 +185,7 @@ class SonatypeServiceTests {
.isThrownBy(() -> this.service.publish(getReleaseInfo(), artifactsRoot)) .isThrownBy(() -> this.service.publish(getReleaseInfo(), artifactsRoot))
.withMessage("Close failed"); .withMessage("Close failed");
this.server.verify(); this.server.verify();
assertThat(uploadRequestsMatcher.candidates).isEmpty(); assertThat(uploadRequestsMatcher.candidates).hasSize(0);
} }
} }

@ -2,13 +2,12 @@
set -ex set -ex
########################################################### ###########################################################
# OS and UTILS # UTILS
########################################################### ###########################################################
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
apt-get update apt-get update
apt-get install --no-install-recommends -y locales tzdata ca-certificates net-tools libxml2-utils git curl libudev1 libxml2-utils iptables iproute2 jq apt-get install --no-install-recommends -y tzdata ca-certificates net-tools libxml2-utils git curl libudev1 libxml2-utils iptables iproute2 jq
locale-gen en_US.utf8
ln -fs /usr/share/zoneinfo/UTC /etc/localtime ln -fs /usr/share/zoneinfo/UTC /etc/localtime
dpkg-reconfigure --frontend noninteractive tzdata dpkg-reconfigure --frontend noninteractive tzdata
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
@ -38,7 +37,6 @@ if [[ $# -eq 2 ]]; then
test -f /opt/openjdk-toolchain/bin/javac test -f /opt/openjdk-toolchain/bin/javac
fi fi
########################################################### ###########################################################
# DOCKER # DOCKER
########################################################### ###########################################################
@ -53,12 +51,3 @@ curl -L https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSI
chmod +x entrykit && \ chmod +x entrykit && \
mv entrykit /bin/entrykit && \ mv entrykit /bin/entrykit && \
entrykit --symlink entrykit --symlink
###########################################################
# DOCKER COMPOSE
###########################################################
mkdir -p /usr/local/lib/docker/cli-plugins
DOCKER_COMPOSE_URL=$( ./get-docker-compose-url.sh )
curl -L ${DOCKER_COMPOSE_URL} -o /usr/local/lib/docker/cli-plugins/docker-compose
chmod +x /usr/local/lib/docker/cli-plugins/docker-compose

@ -3,8 +3,8 @@ github-repo-name: "spring-projects/spring-boot"
homebrew-tap-repo: "https://github.com/spring-io/homebrew-tap.git" homebrew-tap-repo: "https://github.com/spring-io/homebrew-tap.git"
docker-hub-organization: "springci" docker-hub-organization: "springci"
artifactory-server: "https://repo.spring.io" artifactory-server: "https://repo.spring.io"
branch: "main" branch: "2.7.x"
milestone: "3.2.x" milestone: "2.7.x"
build-name: "spring-boot" build-name: "spring-boot"
concourse-url: "https://ci.spring.io" concourse-url: "https://ci.spring.io"
task-timeout: 2h00m task-timeout: 2h00m

@ -169,6 +169,18 @@ resources:
source: source:
<<: *ci-registry-image-resource-source <<: *ci-registry-image-resource-source
repository: ((docker-hub-organization))/spring-boot-ci repository: ((docker-hub-organization))/spring-boot-ci
- name: ci-image-jdk11
type: registry-image
icon: docker
source:
<<: *ci-registry-image-resource-source
repository: ((docker-hub-organization))/spring-boot-ci-jdk11
- name: ci-image-jdk17
type: registry-image
icon: docker
source:
<<: *ci-registry-image-resource-source
repository: ((docker-hub-organization))/spring-boot-ci-jdk17
- name: ci-image-jdk21 - name: ci-image-jdk21
type: registry-image type: registry-image
icon: docker icon: docker
@ -179,8 +191,8 @@ resources:
type: registry-image type: registry-image
icon: docker icon: docker
source: source:
repository: paketobuildpacks/builder-jammy-base repository: paketobuildpacks/builder
tag: latest tag: base
- name: artifactory-repo - name: artifactory-repo
type: artifactory-resource type: artifactory-resource
icon: package-variant icon: package-variant
@ -199,6 +211,22 @@ resources:
access_token: ((github-ci-status-token)) access_token: ((github-ci-status-token))
branch: ((branch)) branch: ((branch))
context: build context: build
- name: repo-status-jdk11-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk11-build
- name: repo-status-jdk17-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk17-build
- name: repo-status-jdk21-build - name: repo-status-jdk21-build
type: github-status-resource type: github-status-resource
icon: eye-check-outline icon: eye-check-outline
@ -242,6 +270,20 @@ jobs:
image: ci-image image: ci-image
vars: vars:
ci-image-name: ci-image ci-image-name: ci-image
- task: build-ci-image-jdk11
privileged: true
file: git-repo/ci/tasks/build-ci-image.yml
output_mapping:
image: ci-image-jdk11
vars:
ci-image-name: ci-image-jdk11
- task: build-ci-image-jdk17
privileged: true
file: git-repo/ci/tasks/build-ci-image.yml
output_mapping:
image: ci-image-jdk17
vars:
ci-image-name: ci-image-jdk17
- task: build-ci-image-jdk21 - task: build-ci-image-jdk21
privileged: true privileged: true
file: git-repo/ci/tasks/build-ci-image.yml file: git-repo/ci/tasks/build-ci-image.yml
@ -253,6 +295,12 @@ jobs:
- put: ci-image - put: ci-image
params: params:
image: ci-image/image.tar image: ci-image/image.tar
- put: ci-image-jdk11
params:
image: ci-image-jdk11/image.tar
- put: ci-image-jdk17
params:
image: ci-image-jdk17/image.tar
- put: ci-image-jdk21 - put: ci-image-jdk21
params: params:
image: ci-image-jdk21/image.tar image: ci-image-jdk21/image.tar
@ -263,6 +311,18 @@ jobs:
trigger: true trigger: true
- get: ci-image - get: ci-image
- in_parallel: - in_parallel:
- task: detect-jdk8-update
image: ci-image
file: git-repo/ci/tasks/detect-jdk-updates.yml
params:
<<: *github-task-params
JDK_VERSION: java8
- task: detect-jdk11-update
image: ci-image
file: git-repo/ci/tasks/detect-jdk-updates.yml
params:
<<: *github-task-params
JDK_VERSION: java11
- task: detect-jdk17-update - task: detect-jdk17-update
image: ci-image image: ci-image
file: git-repo/ci/tasks/detect-jdk-updates.yml file: git-repo/ci/tasks/detect-jdk-updates.yml
@ -334,6 +394,63 @@ jobs:
- put: slack-alert - put: slack-alert
params: params:
<<: *slack-success-params <<: *slack-success-params
- name: jdk11-build
serial: true
public: true
plan:
- get: ci-image-jdk11
- get: git-repo
trigger: true
- put: repo-status-jdk11-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: build-project
image: ci-image-jdk11
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk11-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk11-build
params: { state: "success", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-success-params
- name: jdk17-build
serial: true
public: true
plan:
- get: ci-image-jdk17
- get: git-repo
trigger: true
- put: repo-status-jdk17-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: build-project
image: ci-image-jdk17
privileged: true
timeout: ((task-timeout))
file: git-repo/ci/tasks/build-project.yml
params:
BRANCH: ((branch))
TOOLCHAIN_JAVA_VERSION: 17
<<: *gradle-enterprise-task-params
<<: *docker-hub-task-params
on_failure:
do:
- put: repo-status-jdk17-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk17-build
params: { state: "success", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-success-params
- name: jdk21-build - name: jdk21-build
serial: true serial: true
public: true public: true
@ -636,6 +753,57 @@ jobs:
- put: slack-alert - put: slack-alert
params: params:
<<: *slack-success-params <<: *slack-success-params
- name: jdk11-run-system-tests
serial: true
public: true
plan:
- get: ci-image-jdk11
- get: git-repo
- get: paketo-builder-base-image
trigger: true
- get: daily
trigger: true
- do:
- task: run-system-tests
image: ci-image-jdk11
<<: *run-system-tests-task-params
on_failure:
do:
- put: slack-alert
params:
<<: *slack-fail-params
- put: slack-alert
params:
<<: *slack-success-params
- name: jdk17-run-system-tests
serial: true
public: true
plan:
- get: ci-image-jdk17
- get: git-repo
- get: paketo-builder-base-image
trigger: true
- get: daily
trigger: true
- do:
- task: run-system-tests
image: ci-image-jdk17
privileged: true
timeout: ((task-timeout))
file: git-repo/ci/tasks/run-system-tests.yml
params:
BRANCH: ((branch))
TOOLCHAIN_JAVA_VERSION: 17
<<: *gradle-enterprise-task-params
<<: *docker-hub-task-params
on_failure:
do:
- put: slack-alert
params:
<<: *slack-fail-params
- put: slack-alert
params:
<<: *slack-success-params
- name: jdk21-run-system-tests - name: jdk21-run-system-tests
serial: true serial: true
public: true public: true
@ -667,11 +835,11 @@ jobs:
<<: *slack-success-params <<: *slack-success-params
groups: groups:
- name: "builds" - name: "builds"
jobs: ["build", "jdk21-build", "windows-build"] jobs: ["build", "jdk11-build", "jdk17-build", "jdk21-build", "windows-build"]
- name: "releases" - name: "releases"
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release", "publish-gradle-plugin", "publish-to-sdkman", "update-homebrew-tap"] jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release", "publish-gradle-plugin", "publish-to-sdkman", "update-homebrew-tap"]
- name: "system-tests" - name: "system-tests"
jobs: ["run-system-tests", "jdk21-run-system-tests"] jobs: ["run-system-tests", "jdk11-run-system-tests", "jdk17-run-system-tests", "jdk21-run-system-tests"]
- name: "ci-images" - name: "ci-images"
jobs: ["build-ci-images", "detect-docker-updates", "detect-jdk-updates", "detect-ubuntu-image-updates"] jobs: ["build-ci-images", "detect-docker-updates", "detect-jdk-updates", "detect-ubuntu-image-updates"]

@ -1,4 +1,4 @@
SET "JAVA_HOME=C:\opt\jdk-17" SET "JAVA_HOME=C:\opt\jdk-8"
SET PATH=%PATH%;C:\Program Files\Git\usr\bin SET PATH=%PATH%;C:\Program Files\Git\usr\bin
cd git-repo cd git-repo
.\gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 build .\gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 build

@ -1,6 +1,9 @@
source /opt/concourse-java.sh source /opt/concourse-java.sh
setup_symlinks setup_symlinks
if [[ -d $PWD/embedmongo && ! -d $HOME/.embedmongo ]]; then
ln -s "$PWD/embedmongo" "$HOME/.embedmongo"
fi
cleanup_maven_repo "org.springframework.boot" cleanup_maven_repo "org.springframework.boot"

@ -8,9 +8,17 @@ report_error() {
trap 'report_error $? $LINENO' ERR trap 'report_error $? $LINENO' ERR
case "$JDK_VERSION" in case "$JDK_VERSION" in
java8)
BASE_URL="https://api.bell-sw.com/v1/liberica/releases?version-feature=8"
ISSUE_TITLE="Upgrade Java 8 version in CI image and .sdkmanrc"
;;
java11)
BASE_URL="https://api.bell-sw.com/v1/liberica/releases?version-feature=11"
ISSUE_TITLE="Upgrade Java 11 version in CI image"
;;
java17) java17)
BASE_URL="https://api.bell-sw.com/v1/liberica/releases?version-feature=17" BASE_URL="https://api.bell-sw.com/v1/liberica/releases?version-feature=17"
ISSUE_TITLE="Upgrade Java 17 version in CI image and .sdkmanrc" ISSUE_TITLE="Upgrade Java 17 version in CI image"
;; ;;
java21) java21)
BASE_URL="https://api.bell-sw.com/v1/liberica/releases?version-feature=21" BASE_URL="https://api.bell-sw.com/v1/liberica/releases?version-feature=21"

@ -7,7 +7,7 @@ git clone homebrew-tap-repo updated-homebrew-tap-repo > /dev/null
if [[ $LATEST_GA = true ]]; then if [[ $LATEST_GA = true ]]; then
pushd updated-homebrew-tap-repo > /dev/null pushd updated-homebrew-tap-repo > /dev/null
curl https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-cli/${version}/spring-boot-cli-${version}-homebrew.rb --output spring-boot-cli-${version}-homebrew.rb curl https://repo.spring.io/libs-release-local/org/springframework/boot/spring-boot-cli/${version}/spring-boot-cli-${version}-homebrew.rb --output spring-boot-cli-${version}-homebrew.rb
rm spring-boot.rb rm spring-boot.rb
mv spring-boot-cli-*.rb spring-boot.rb mv spring-boot-cli-*.rb spring-boot.rb
git config user.name "Spring Builds" > /dev/null git config user.name "Spring Builds" > /dev/null

@ -8,6 +8,7 @@ outputs:
caches: caches:
- path: gradle - path: gradle
- path: maven - path: maven
- path: embedmongo
params: params:
BRANCH: BRANCH:
CI: true CI: true

@ -11,8 +11,8 @@
xmlns:setup.workingsets="http://www.eclipse.org/oomph/setup/workingsets/1.0" xmlns:setup.workingsets="http://www.eclipse.org/oomph/setup/workingsets/1.0"
xmlns:workingsets="http://www.eclipse.org/oomph/workingsets/1.0" xmlns:workingsets="http://www.eclipse.org/oomph/workingsets/1.0"
xsi:schemaLocation="http://www.eclipse.org/oomph/setup/jdt/1.0 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/JDT.ecore http://www.eclipse.org/buildship/oomph/1.0 https://raw.githubusercontent.com/eclipse/buildship/master/org.eclipse.buildship.oomph/model/GradleImport-1.0.ecore http://www.eclipse.org/oomph/predicates/1.0 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/Predicates.ecore http://www.eclipse.org/oomph/setup/workingsets/1.0 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/SetupWorkingSets.ecore http://www.eclipse.org/oomph/workingsets/1.0 http://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 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/JDT.ecore http://www.eclipse.org/buildship/oomph/1.0 https://raw.githubusercontent.com/eclipse/buildship/master/org.eclipse.buildship.oomph/model/GradleImport-1.0.ecore http://www.eclipse.org/oomph/predicates/1.0 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/Predicates.ecore http://www.eclipse.org/oomph/setup/workingsets/1.0 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/SetupWorkingSets.ecore http://www.eclipse.org/oomph/workingsets/1.0 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/WorkingSets.ecore"
name="spring.boot.3.2.x" name="spring.boot.2.7.x"
label="Spring Boot 3.2.x"> label="Spring Boot 2.7.x">
<setupTask <setupTask
xsi:type="setup:VariableTask" xsi:type="setup:VariableTask"
type="FOLDER" type="FOLDER"
@ -22,8 +22,8 @@
label="Checkout Location"/> label="Checkout Location"/>
<setupTask <setupTask
xsi:type="jdt:JRETask" xsi:type="jdt:JRETask"
version="JavaSE-17" version="JavaSE-1.8"
location="${jre.location-17}"> location="${jre.location-1.8}">
<description> <description>
Define the JRE needed to compile and run the Java Define the JRE needed to compile and run the Java
projects of ${scope.project.label} projects of ${scope.project.label}
@ -107,14 +107,14 @@
</setupTask> </setupTask>
<setupTask <setupTask
xsi:type="oomph:GradleImportTask" xsi:type="oomph:GradleImportTask"
javaHome="${jre.location-17}"> javaHome="${jre.location-1.8}">
<sourceLocator <sourceLocator
rootFolder="${checkout.location}" rootFolder="${checkout.location}"
locateNestedProjects="true"/> locateNestedProjects="true"/>
</setupTask> </setupTask>
<setupTask <setupTask
xsi:type="oomph:GradleImportTask" xsi:type="oomph:GradleImportTask"
javaHome="${jre.location-17}"> javaHome="${jre.location-1.8}">
<sourceLocator <sourceLocator
rootFolder="${checkout.location}/buildSrc"/> rootFolder="${checkout.location}/buildSrc"/>
</setupTask> </setupTask>
@ -136,7 +136,7 @@
name="spring-boot-tools"> name="spring-boot-tools">
<predicate <predicate
xsi:type="predicates:NamePredicate" xsi:type="predicates:NamePredicate"
pattern="spring-boot-(tools|antlib|configuration-.*|loader|loader-classic|.*-tools|.*-layertools|.*-plugin|autoconfigure-processor|buildpack.*)"/> pattern="spring-boot-(tools|antlib|configuration-.*|loader|.*-tools|.*-layertools|.*-plugin|autoconfigure-processor|buildpack.*)"/>
</workingSet> </workingSet>
<workingSet <workingSet
name="spring-boot-starters"> name="spring-boot-starters">

@ -4,13 +4,13 @@ require 'net/http'
require 'yaml' require 'yaml'
require 'logger' require 'logger'
$main_branch = "3.2.x" $main_branch = "2.7.x"
$log = Logger.new(STDOUT) $log = Logger.new(STDOUT)
$log.level = Logger::WARN $log.level = Logger::WARN
def get_fixed_issues() def get_fixed_issues()
$log.debug "Searching for forward merge" $log.debug "Searching for for forward merge"
rev=`git rev-parse -q --verify MERGE_HEAD`.strip rev=`git rev-parse -q --verify MERGE_HEAD`.strip
$log.debug "Found #{rev} from git rev-parse" $log.debug "Found #{rev} from git rev-parse"
return nil unless rev return nil unless rev
@ -65,7 +65,7 @@ if message_type != "merge"
exit 0; exit 0;
end end
$log.debug "Searching for forward merge" $log.debug "Searching for for forward merge"
fixed = get_fixed_issues() fixed = get_fixed_issues()
rewritten_message = rewrite_message(message_file, fixed) rewritten_message = rewrite_message(message_file, fixed)
File.write(message_file, rewritten_message) File.write(message_file, rewritten_message)

@ -1,18 +1,17 @@
version=3.2.0-SNAPSHOT version=2.7.17-SNAPSHOT
org.gradle.caching=true org.gradle.caching=true
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8
assertjVersion=3.24.2 assertjVersion=3.22.0
commonsCodecVersion=1.16.0 commonsCodecVersion=1.15
hamcrestVersion=2.2 hamcrestVersion=2.2
jacksonVersion=2.15.2 jacksonVersion=2.13.5
junitJupiterVersion=5.10.0 junitJupiterVersion=5.8.2
kotlinVersion=1.9.10 kotlinVersion=1.6.21
mavenVersion=3.9.4 mavenVersion=3.9.4
nativeBuildToolsVersion=0.9.27 springFrameworkVersion=5.3.30
springFrameworkVersion=6.1.0-SNAPSHOT tomcatVersion=9.0.80
tomcatVersion=10.1.13
kotlin.stdlib.default.dependency=false kotlin.stdlib.default.dependency=false

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-bin.zip
networkTimeout=10000 networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

@ -5,7 +5,6 @@ pluginManagement {
if (version.endsWith('-SNAPSHOT')) { if (version.endsWith('-SNAPSHOT')) {
maven { url "https://repo.spring.io/snapshot" } maven { url "https://repo.spring.io/snapshot" }
} }
} }
resolutionStrategy { resolutionStrategy {
eachPlugin { eachPlugin {
@ -26,8 +25,6 @@ plugins {
rootProject.name="spring-boot-build" rootProject.name="spring-boot-build"
enableFeaturePreview("STABLE_CONFIGURATION_CACHE")
settings.gradle.projectsLoaded { settings.gradle.projectsLoaded {
gradleEnterprise { gradleEnterprise {
buildScan { buildScan {
@ -52,33 +49,28 @@ 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-antlib"
include "spring-boot-project:spring-boot-tools:spring-boot-autoconfigure-processor" include "spring-boot-project:spring-boot-tools:spring-boot-autoconfigure-processor"
include "spring-boot-project:spring-boot-tools:spring-boot-buildpack-platform" include "spring-boot-project:spring-boot-tools:spring-boot-buildpack-platform"
include "spring-boot-project:spring-boot-tools:spring-boot-cli"
include "spring-boot-project:spring-boot-tools:spring-boot-configuration-metadata" include "spring-boot-project:spring-boot-tools:spring-boot-configuration-metadata"
include "spring-boot-project:spring-boot-tools:spring-boot-configuration-metadata-changelog-generator"
include "spring-boot-project:spring-boot-tools:spring-boot-configuration-processor" 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-gradle-plugin"
include "spring-boot-project:spring-boot-tools:spring-boot-gradle-test-support" include "spring-boot-project:spring-boot-tools:spring-boot-gradle-test-support"
include "spring-boot-project:spring-boot-tools:spring-boot-jarmode-layertools" include "spring-boot-project:spring-boot-tools:spring-boot-jarmode-layertools"
include "spring-boot-project:spring-boot-tools:spring-boot-loader" include "spring-boot-project:spring-boot-tools:spring-boot-loader"
include "spring-boot-project:spring-boot-tools:spring-boot-loader-classic"
include "spring-boot-project:spring-boot-tools:spring-boot-loader-tools" 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-maven-plugin"
include "spring-boot-project:spring-boot-tools:spring-boot-properties-migrator"
include "spring-boot-project:spring-boot-tools:spring-boot-test-support" include "spring-boot-project:spring-boot-tools:spring-boot-test-support"
include "spring-boot-project:spring-boot" include "spring-boot-project:spring-boot"
include "spring-boot-project:spring-boot-autoconfigure" include "spring-boot-project:spring-boot-autoconfigure"
include "spring-boot-project:spring-boot-actuator" include "spring-boot-project:spring-boot-actuator"
include "spring-boot-project:spring-boot-actuator-autoconfigure" include "spring-boot-project:spring-boot-actuator-autoconfigure"
include "spring-boot-project:spring-boot-docker-compose" include "spring-boot-project:spring-boot-cli"
include "spring-boot-project:spring-boot-devtools" include "spring-boot-project:spring-boot-devtools"
include "spring-boot-project:spring-boot-docs" 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"
include "spring-boot-project:spring-boot-testcontainers"
include "spring-boot-project:spring-boot-test-autoconfigure" include "spring-boot-project:spring-boot-test-autoconfigure"
include "spring-boot-tests:spring-boot-integration-tests:spring-boot-configuration-processor-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-launch-script-tests"
include "spring-boot-tests:spring-boot-integration-tests:spring-boot-loader-tests" include "spring-boot-tests:spring-boot-integration-tests:spring-boot-loader-tests"
include "spring-boot-tests:spring-boot-integration-tests:spring-boot-loader-classic-tests"
include "spring-boot-tests:spring-boot-integration-tests:spring-boot-server-tests" include "spring-boot-tests:spring-boot-integration-tests:spring-boot-server-tests"
include "spring-boot-system-tests:spring-boot-deployment-tests" include "spring-boot-system-tests:spring-boot-deployment-tests"
include "spring-boot-system-tests:spring-boot-image-tests" include "spring-boot-system-tests:spring-boot-image-tests"

@ -2,7 +2,6 @@ plugins {
id "java-library" id "java-library"
id "org.asciidoctor.jvm.convert" id "org.asciidoctor.jvm.convert"
id "org.springframework.boot.auto-configuration" id "org.springframework.boot.auto-configuration"
id "org.springframework.boot.configuration-properties"
id "org.springframework.boot.conventions" id "org.springframework.boot.conventions"
id "org.springframework.boot.deployed" id "org.springframework.boot.deployed"
id "org.springframework.boot.optional-dependencies" id "org.springframework.boot.optional-dependencies"
@ -19,6 +18,7 @@ dependencies {
asciidoctorExtensions("io.spring.asciidoctor:spring-asciidoctor-extensions-section-ids") asciidoctorExtensions("io.spring.asciidoctor:spring-asciidoctor-extensions-section-ids")
api(project(":spring-boot-project:spring-boot-actuator")) api(project(":spring-boot-project:spring-boot-actuator"))
api(project(":spring-boot-project:spring-boot")) api(project(":spring-boot-project:spring-boot"))
api(project(":spring-boot-project:spring-boot-autoconfigure")) api(project(":spring-boot-project:spring-boot-autoconfigure"))
@ -33,15 +33,11 @@ dependencies {
optional("com.github.ben-manes.caffeine:caffeine") optional("com.github.ben-manes.caffeine:caffeine")
optional("com.hazelcast:hazelcast") optional("com.hazelcast:hazelcast")
optional("com.hazelcast:hazelcast-spring") optional("com.hazelcast:hazelcast-spring")
optional("com.sun.mail:jakarta.mail")
optional("com.zaxxer:HikariCP") optional("com.zaxxer:HikariCP")
optional("io.dropwizard.metrics:metrics-jmx") optional("io.dropwizard.metrics:metrics-jmx")
optional("io.lettuce:lettuce-core") optional("io.lettuce:lettuce-core")
optional("io.micrometer:micrometer-observation") optional("io.micrometer:micrometer-core")
optional("io.micrometer:micrometer-jakarta9")
optional("io.micrometer:micrometer-tracing")
optional("io.micrometer:micrometer-tracing-bridge-brave")
optional("io.micrometer:micrometer-tracing-bridge-otel")
optional("io.micrometer:micrometer-tracing-reporter-wavefront")
optional("io.micrometer:micrometer-registry-appoptics") optional("io.micrometer:micrometer-registry-appoptics")
optional("io.micrometer:micrometer-registry-atlas") { optional("io.micrometer:micrometer-registry-atlas") {
exclude group: "javax.inject", module: "javax.inject" exclude group: "javax.inject", module: "javax.inject"
@ -56,7 +52,6 @@ dependencies {
optional("io.micrometer:micrometer-registry-jmx") optional("io.micrometer:micrometer-registry-jmx")
optional("io.micrometer:micrometer-registry-kairos") optional("io.micrometer:micrometer-registry-kairos")
optional("io.micrometer:micrometer-registry-new-relic") optional("io.micrometer:micrometer-registry-new-relic")
optional("io.micrometer:micrometer-registry-otlp")
optional("io.micrometer:micrometer-registry-prometheus") optional("io.micrometer:micrometer-registry-prometheus")
optional("io.micrometer:micrometer-registry-stackdriver") { optional("io.micrometer:micrometer-registry-stackdriver") {
exclude group: "commons-logging", module: "commons-logging" exclude group: "commons-logging", module: "commons-logging"
@ -68,47 +63,59 @@ dependencies {
optional("io.micrometer:micrometer-registry-signalfx") optional("io.micrometer:micrometer-registry-signalfx")
optional("io.micrometer:micrometer-registry-statsd") optional("io.micrometer:micrometer-registry-statsd")
optional("io.micrometer:micrometer-registry-wavefront") optional("io.micrometer:micrometer-registry-wavefront")
optional("io.zipkin.reporter2:zipkin-reporter-brave")
optional("io.zipkin.reporter2:zipkin-sender-urlconnection")
optional("io.opentelemetry:opentelemetry-exporter-zipkin")
optional("io.opentelemetry:opentelemetry-exporter-otlp")
optional("io.projectreactor.netty:reactor-netty-http") optional("io.projectreactor.netty:reactor-netty-http")
optional("io.r2dbc:r2dbc-pool") optional("io.r2dbc:r2dbc-pool")
optional("io.r2dbc:r2dbc-proxy")
optional("io.r2dbc:r2dbc-spi") optional("io.r2dbc:r2dbc-spi")
optional("jakarta.jms:jakarta.jms-api") optional("jakarta.jms:jakarta.jms-api")
optional("jakarta.persistence:jakarta.persistence-api") optional("jakarta.persistence:jakarta.persistence-api")
optional("jakarta.servlet:jakarta.servlet-api") optional("jakarta.servlet:jakarta.servlet-api")
optional("javax.cache:cache-api") optional("javax.cache:cache-api")
optional("org.apache.activemq:activemq-client-jakarta") optional("net.sf.ehcache:ehcache")
optional("org.apache.activemq:activemq-broker") {
exclude group: "org.apache.geronimo.specs", module: "geronimo-jms_1.1_spec"
exclude group: "org.apache.geronimo.specs", module: "geronimo-j2ee-management_1.1_spec"
}
optional("org.apache.commons:commons-dbcp2") { optional("org.apache.commons:commons-dbcp2") {
exclude group: "commons-logging", module: "commons-logging" exclude group: "commons-logging", module: "commons-logging"
} }
optional("org.apache.kafka:kafka-clients") optional("org.apache.kafka:kafka-clients")
optional("org.apache.kafka:kafka-streams") optional("org.apache.kafka:kafka-streams")
optional("org.apache.logging.log4j:log4j-api") optional("org.apache.logging.log4j:log4j-api")
optional("org.apache.solr:solr-solrj") {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
}
optional("org.apache.tomcat.embed:tomcat-embed-core") optional("org.apache.tomcat.embed:tomcat-embed-core")
optional("org.apache.tomcat.embed:tomcat-embed-el") optional("org.apache.tomcat.embed:tomcat-embed-el")
optional("org.apache.tomcat:tomcat-jdbc") optional("org.apache.tomcat:tomcat-jdbc")
optional("org.aspectj:aspectjweaver") optional("org.aspectj:aspectjweaver")
optional("org.cache2k:cache2k-micrometer") optional("org.cache2k:cache2k-micrometer")
optional("org.cache2k:cache2k-spring") optional("org.cache2k:cache2k-spring")
optional("org.eclipse.angus:angus-mail")
optional("org.eclipse.jetty:jetty-server") { optional("org.eclipse.jetty:jetty-server") {
exclude group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api" exclude group: "javax.servlet", module: "javax.servlet-api"
} }
optional("org.elasticsearch:elasticsearch")
optional("org.elasticsearch.client:elasticsearch-rest-client") { optional("org.elasticsearch.client:elasticsearch-rest-client") {
exclude group: "commons-logging", module: "commons-logging" exclude group: "commons-logging", module: "commons-logging"
} }
optional("org.flywaydb:flyway-core") optional("org.flywaydb:flyway-core")
optional("org.glassfish.jersey.core:jersey-server") optional("org.glassfish.jersey.core:jersey-server")
optional("org.glassfish.jersey.containers:jersey-container-servlet-core") optional("org.glassfish.jersey.containers:jersey-container-servlet-core")
optional("org.hibernate.orm:hibernate-core") optional("org.hibernate:hibernate-core") {
optional("org.hibernate.orm:hibernate-micrometer") exclude group: "javax.activation", module: "javax.activation-api"
exclude group: "javax.persistence", module: "javax.persistence-api"
exclude group: "javax.xml.bind", module: "jaxb-api"
exclude group: "org.jboss.spec.javax.transaction", module: "jboss-transaction-api_1.2_spec"
}
optional("org.hibernate:hibernate-micrometer") {
exclude group: "javax.activation", module: "javax.activation-api"
exclude group: "javax.persistence", module: "javax.persistence-api"
exclude group: "javax.xml.bind", module: "jaxb-api"
exclude group: "org.jboss.spec.javax.transaction", module: "jboss-transaction-api_1.2_spec"
}
optional("org.hibernate.validator:hibernate-validator") optional("org.hibernate.validator:hibernate-validator")
optional("org.influxdb:influxdb-java") optional("org.influxdb:influxdb-java")
optional("org.jolokia:jolokia-core")
optional("org.liquibase:liquibase-core") { optional("org.liquibase:liquibase-core") {
exclude group: "javax.activation", module: "javax.activation-api"
exclude group: "javax.xml.bind", module: "jaxb-api" exclude group: "javax.xml.bind", module: "jaxb-api"
} }
optional("org.mongodb:mongodb-driver-reactivestreams") optional("org.mongodb:mongodb-driver-reactivestreams")
@ -121,11 +128,13 @@ dependencies {
optional("org.springframework:spring-webflux") optional("org.springframework:spring-webflux")
optional("org.springframework:spring-webmvc") optional("org.springframework:spring-webmvc")
optional("org.springframework.amqp:spring-rabbit") optional("org.springframework.amqp:spring-rabbit")
optional("org.springframework.batch:spring-batch-core")
optional("org.springframework.data:spring-data-cassandra") { optional("org.springframework.data:spring-data-cassandra") {
exclude group: "org.slf4j", module: "jcl-over-slf4j" exclude group: "org.slf4j", module: "jcl-over-slf4j"
} }
optional("org.springframework.data:spring-data-couchbase") optional("org.springframework.data:spring-data-couchbase") {
exclude group: "com.querydsl", module: "querydsl-apt"
exclude group: "javax.annotation", module: "javax.annotation-api"
}
optional("org.springframework.data:spring-data-jpa") optional("org.springframework.data:spring-data-jpa")
optional("org.springframework.data:spring-data-ldap") optional("org.springframework.data:spring-data-ldap")
optional("org.springframework.data:spring-data-mongodb") optional("org.springframework.data:spring-data-mongodb")
@ -143,27 +152,25 @@ dependencies {
testImplementation(project(":spring-boot-project:spring-boot-test")) 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-tools:spring-boot-test-support"))
testImplementation("io.micrometer:micrometer-observation-test")
testImplementation("io.projectreactor:reactor-test") testImplementation("io.projectreactor:reactor-test")
testImplementation("io.r2dbc:r2dbc-h2") testImplementation("io.r2dbc:r2dbc-h2")
testImplementation("com.squareup.okhttp3:mockwebserver") testImplementation("com.squareup.okhttp3:mockwebserver")
testImplementation("com.jayway.jsonpath:json-path") testImplementation("com.jayway.jsonpath:json-path")
testImplementation("io.undertow:undertow-core") testImplementation("io.undertow:undertow-core")
testImplementation("io.undertow:undertow-servlet") testImplementation("io.undertow:undertow-servlet") {
testImplementation("jakarta.xml.bind:jakarta.xml.bind-api") exclude group: "org.jboss.spec.javax.annotation", module: "jboss-annotations-api_1.3_spec"
testImplementation("org.apache.activemq:artemis-jakarta-client") { exclude group: "org.jboss.spec.javax.servlet", module: "jboss-servlet-api_4.0_spec"
exclude group: "commons-logging", module: "commons-logging"
}
testImplementation("org.apache.activemq:artemis-jakarta-server") {
exclude group: "commons-logging", module: "commons-logging"
} }
testImplementation("jakarta.xml.bind:jakarta.xml.bind-api")
testImplementation("org.apache.logging.log4j:log4j-to-slf4j") testImplementation("org.apache.logging.log4j:log4j-to-slf4j")
testImplementation("org.aspectj:aspectjrt") testImplementation("org.aspectj:aspectjrt")
testImplementation("org.assertj:assertj-core") testImplementation("org.assertj:assertj-core")
testImplementation("org.awaitility:awaitility") testImplementation("org.awaitility:awaitility")
testImplementation("org.cache2k:cache2k-api") testImplementation("org.cache2k:cache2k-api")
testImplementation("org.eclipse.jetty.ee10:jetty-ee10-webapp") testImplementation("org.eclipse.jetty:jetty-webapp") {
testImplementation("org.glassfish.jersey.ext:jersey-spring6") exclude group: "javax.servlet", module: "javax.servlet-api"
}
testImplementation("org.glassfish.jersey.ext:jersey-spring5")
testImplementation("org.glassfish.jersey.media:jersey-media-json-jackson") testImplementation("org.glassfish.jersey.media:jersey-media-json-jackson")
testImplementation("org.hamcrest:hamcrest") testImplementation("org.hamcrest:hamcrest")
testImplementation("org.hsqldb:hsqldb") testImplementation("org.hsqldb:hsqldb")
@ -171,11 +178,12 @@ dependencies {
testImplementation("org.mockito:mockito-core") testImplementation("org.mockito:mockito-core")
testImplementation("org.mockito:mockito-junit-jupiter") testImplementation("org.mockito:mockito-junit-jupiter")
testImplementation("org.skyscreamer:jsonassert") testImplementation("org.skyscreamer:jsonassert")
testImplementation("org.springframework:spring-core-test")
testImplementation("org.springframework:spring-orm") testImplementation("org.springframework:spring-orm")
testImplementation("org.springframework.data:spring-data-rest-webmvc") testImplementation("org.springframework.data:spring-data-rest-webmvc")
testImplementation("org.springframework.integration:spring-integration-jmx") testImplementation("org.springframework.integration:spring-integration-jmx")
testImplementation("org.springframework.restdocs:spring-restdocs-mockmvc") testImplementation("org.springframework.restdocs:spring-restdocs-mockmvc") {
exclude group: "javax.servlet", module: "javax.servlet-api"
}
testImplementation("org.springframework.restdocs:spring-restdocs-webtestclient") testImplementation("org.springframework.restdocs:spring-restdocs-webtestclient")
testImplementation("org.springframework.security:spring-security-test") testImplementation("org.springframework.security:spring-security-test")
testImplementation("org.yaml:snakeyaml") testImplementation("org.yaml:snakeyaml")
@ -183,17 +191,9 @@ dependencies {
testRuntimeOnly("jakarta.management.j2ee:jakarta.management.j2ee-api") testRuntimeOnly("jakarta.management.j2ee:jakarta.management.j2ee-api")
testRuntimeOnly("jakarta.transaction:jakarta.transaction-api") testRuntimeOnly("jakarta.transaction:jakarta.transaction-api")
testRuntimeOnly("org.cache2k:cache2k-core") testRuntimeOnly("org.cache2k:cache2k-core")
testRuntimeOnly("org.opensaml:opensaml-core:4.0.1")
testRuntimeOnly("org.opensaml:opensaml-saml-api:4.0.1")
testRuntimeOnly("org.opensaml:opensaml-saml-impl:4.0.1")
testRuntimeOnly("org.springframework:spring-aspects")
testRuntimeOnly("org.springframework.security:spring-security-oauth2-jose") testRuntimeOnly("org.springframework.security:spring-security-oauth2-jose")
testRuntimeOnly("org.springframework.security:spring-security-oauth2-resource-server") testRuntimeOnly("org.springframework.security:spring-security-oauth2-resource-server")
testRuntimeOnly("org.springframework.security:spring-security-saml2-service-provider") { testRuntimeOnly("org.springframework.security:spring-security-saml2-service-provider")
exclude group: "org.opensaml", module: "opensaml-core"
exclude group: "org.opensaml", module: "opensaml-saml-api"
exclude group: "org.opensaml", module: "opensaml-saml-impl"
}
} }
task dependencyVersions(type: org.springframework.boot.build.constraints.ExtractVersionConstraints) { task dependencyVersions(type: org.springframework.boot.build.constraints.ExtractVersionConstraints) {
@ -229,7 +229,6 @@ artifacts {
} }
tasks.named("test") { tasks.named("test") {
jvmArgs += "--add-opens=java.base/java.net=ALL-UNNAMED"
filter { filter {
excludeTestsMatching("org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation.*") excludeTestsMatching("org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation.*")
} }
@ -239,7 +238,6 @@ def documentationTest = tasks.register("documentationTest", Test) {
filter { filter {
includeTestsMatching("org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation.*") includeTestsMatching("org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation.*")
} }
jvmArgs += "--add-opens=java.base/java.net=ALL-UNNAMED"
outputs.dir("${buildDir}/generated-snippets") outputs.dir("${buildDir}/generated-snippets")
predictiveSelection { predictiveSelection {
enabled = false enabled = false

@ -132,7 +132,3 @@ threaddump=threaddump
threaddump-retrieving-json=threaddump.retrieving-json threaddump-retrieving-json=threaddump.retrieving-json
threaddump-retrieving-json-response-structure=threaddump.retrieving-json.response-structure threaddump-retrieving-json-response-structure=threaddump.retrieving-json.response-structure
threaddump-retrieving-text=threaddump.retrieving-text threaddump-retrieving-text=threaddump.retrieving-text
http-trace=httpexchanges
http-trace.retrieving=httpexchanges.retrieving
http-trace.retrieving.response-structure=httpexchanges.retrieving.response-structure

@ -23,7 +23,7 @@ The endpoint uses query parameters to limit the events that it returns.
The following table shows the supported query parameters: The following table shows the supported query parameters:
[cols="2,4"] [cols="2,4"]
include::{snippets}/auditevents/filtered/query-parameters.adoc[] include::{snippets}/auditevents/filtered/request-parameters.adoc[]

@ -46,7 +46,7 @@ Otherwise, the `cacheManager` must be specified.
The following table shows the supported query parameters: The following table shows the supported query parameters:
[cols="2,4"] [cols="2,4"]
include::{snippets}/caches/named/query-parameters.adoc[] include::{snippets}/caches/named/request-parameters.adoc[]
@ -85,4 +85,4 @@ Otherwise, the `cacheManager` must be specified.
The following table shows the supported query parameters: The following table shows the supported query parameters:
[cols="2,4"] [cols="2,4"]
include::{snippets}/caches/evict-named/query-parameters.adoc[] include::{snippets}/caches/evict-named/request-parameters.adoc[]

@ -1,25 +0,0 @@
[[httpexchanges]]
= HTTP Exchanges (`httpexchanges`)
The `httpexchanges` endpoint provides information about HTTP request-response exchanges.
[[httpexchanges.retrieving]]
== Retrieving the HTTP Exchanges
To retrieve the HTTP exchanges, make a `GET` request to `/actuator/httpexchanges`, as shown in the following curl-based example:
include::{snippets}/httpexchanges/curl-request.adoc[]
The resulting response is similar to the following:
include::{snippets}/httpexchanges/http-response.adoc[]
[[httpexchanges.retrieving.response-structure]]
=== Response Structure
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}/httpexchanges/response-fields.adoc[]

@ -0,0 +1,25 @@
[[http-trace]]
= HTTP Trace (`httptrace`)
The `httptrace` endpoint provides information about HTTP request-response exchanges.
[[http-trace.retrieving]]
== Retrieving the Traces
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[]
The resulting response is similar to the following:
include::{snippets}/httptrace/http-response.adoc[]
[[http-trace.retrieving.response-structure]]
=== Response Structure
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[]

@ -45,7 +45,7 @@ The endpoint uses query parameters to <<metrics.drilling-down,drill down>> into
The following table shows the single supported query parameter: The following table shows the single supported query parameter:
[cols="2,4"] [cols="2,4"]
include::{snippets}/metrics/metric-with-tags/query-parameters.adoc[] include::{snippets}/metrics/metric-with-tags/request-parameters.adoc[]

@ -31,7 +31,7 @@ The endpoint uses query parameters to limit the samples that it returns.
The following table shows the supported query parameters: The following table shows the supported query parameters:
[cols="2,4"] [cols="2,4"]
include::{snippets}/prometheus/names/query-parameters.adoc[] include::{snippets}/prometheus/names/request-parameters.adoc[]

@ -23,7 +23,7 @@ The endpoint uses query parameters to limit the sessions that it returns.
The following table shows the single required query parameter: The following table shows the single required query parameter:
[cols="2,4"] [cols="2,4"]
include::{snippets}/sessions/username/query-parameters.adoc[] include::{snippets}/sessions/username/request-parameters.adoc[]

@ -72,7 +72,7 @@ include::endpoints/health.adoc[leveloffset=+1]
include::endpoints/heapdump.adoc[leveloffset=+1] include::endpoints/heapdump.adoc[leveloffset=+1]
include::endpoints/httpexchanges.adoc[leveloffset=+1] include::endpoints/httptrace.adoc[leveloffset=+1]
include::endpoints/info.adoc[leveloffset=+1] include::endpoints/info.adoc[leveloffset=+1]

@ -78,6 +78,18 @@ public abstract class OnEndpointElementCondition extends SpringBootCondition {
* @since 2.6.0 * @since 2.6.0
*/ */
protected ConditionOutcome getDefaultOutcome(ConditionContext context, AnnotationAttributes annotationAttributes) { protected ConditionOutcome getDefaultOutcome(ConditionContext context, AnnotationAttributes annotationAttributes) {
return getDefaultEndpointsOutcome(context);
}
/**
* Return the default outcome that should be used.
* @param context the condition context
* @return the default outcome
* @deprecated since 2.6.0 for removal in 3.0.0 in favor of
* {@link #getDefaultOutcome(ConditionContext, AnnotationAttributes)}
*/
@Deprecated
protected ConditionOutcome getDefaultEndpointsOutcome(ConditionContext context) {
boolean match = Boolean boolean match = Boolean
.parseBoolean(context.getEnvironment().getProperty(this.prefix + "defaults.enabled", "true")); .parseBoolean(context.getEnvironment().getProperty(this.prefix + "defaults.enabled", "true"));
return new ConditionOutcome(match, ConditionMessage.forCondition(this.annotationType) return new ConditionOutcome(match, ConditionMessage.forCondition(this.annotationType)

@ -44,10 +44,6 @@ import org.springframework.context.annotation.Bean;
public class RabbitHealthContributorAutoConfiguration public class RabbitHealthContributorAutoConfiguration
extends CompositeHealthContributorConfiguration<RabbitHealthIndicator, RabbitTemplate> { extends CompositeHealthContributorConfiguration<RabbitHealthIndicator, RabbitTemplate> {
public RabbitHealthContributorAutoConfiguration() {
super(RabbitHealthIndicator::new);
}
@Bean @Bean
@ConditionalOnMissingBean(name = { "rabbitHealthIndicator", "rabbitHealthContributor" }) @ConditionalOnMissingBean(name = { "rabbitHealthIndicator", "rabbitHealthContributor" })
public HealthContributor rabbitHealthContributor(Map<String, RabbitTemplate> rabbitTemplates) { public HealthContributor rabbitHealthContributor(Map<String, RabbitTemplate> rabbitTemplates) {

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2022 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -43,10 +43,6 @@ class CassandraHealthContributorConfigurations {
static class CassandraDriverConfiguration static class CassandraDriverConfiguration
extends CompositeHealthContributorConfiguration<CassandraDriverHealthIndicator, CqlSession> { extends CompositeHealthContributorConfiguration<CassandraDriverHealthIndicator, CqlSession> {
CassandraDriverConfiguration() {
super(CassandraDriverHealthIndicator::new);
}
@Bean @Bean
@ConditionalOnMissingBean(name = { "cassandraHealthIndicator", "cassandraHealthContributor" }) @ConditionalOnMissingBean(name = { "cassandraHealthIndicator", "cassandraHealthContributor" })
HealthContributor cassandraHealthContributor(Map<String, CqlSession> sessions) { HealthContributor cassandraHealthContributor(Map<String, CqlSession> sessions) {
@ -60,10 +56,6 @@ class CassandraHealthContributorConfigurations {
static class CassandraReactiveDriverConfiguration extends static class CassandraReactiveDriverConfiguration extends
CompositeReactiveHealthContributorConfiguration<CassandraDriverReactiveHealthIndicator, CqlSession> { CompositeReactiveHealthContributorConfiguration<CassandraDriverReactiveHealthIndicator, CqlSession> {
CassandraReactiveDriverConfiguration() {
super(CassandraDriverReactiveHealthIndicator::new);
}
@Bean @Bean
@ConditionalOnMissingBean(name = { "cassandraHealthIndicator", "cassandraHealthContributor" }) @ConditionalOnMissingBean(name = { "cassandraHealthIndicator", "cassandraHealthContributor" })
ReactiveHealthContributor cassandraHealthContributor(Map<String, CqlSession> sessions) { ReactiveHealthContributor cassandraHealthContributor(Map<String, CqlSession> sessions) {

@ -19,10 +19,6 @@ package org.springframework.boot.actuate.autoconfigure.cloudfoundry;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryWebEndpointDiscoverer.CloudFoundryWebEndpointDiscovererRuntimeHints;
import org.springframework.boot.actuate.endpoint.EndpointFilter; import org.springframework.boot.actuate.endpoint.EndpointFilter;
import org.springframework.boot.actuate.endpoint.invoke.OperationInvokerAdvisor; import org.springframework.boot.actuate.endpoint.invoke.OperationInvokerAdvisor;
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper; import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
@ -33,7 +29,6 @@ import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExten
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer; import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations;
/** /**
@ -43,7 +38,6 @@ import org.springframework.core.annotation.MergedAnnotations;
* @author Madhura Bhave * @author Madhura Bhave
* @since 2.0.0 * @since 2.0.0
*/ */
@ImportRuntimeHints(CloudFoundryWebEndpointDiscovererRuntimeHints.class)
public class CloudFoundryWebEndpointDiscoverer extends WebEndpointDiscoverer { public class CloudFoundryWebEndpointDiscoverer extends WebEndpointDiscoverer {
/** /**
@ -84,14 +78,4 @@ public class CloudFoundryWebEndpointDiscoverer extends WebEndpointDiscoverer {
return MergedAnnotations.from(extensionBeanType).isPresent(EndpointCloudFoundryExtension.class); return MergedAnnotations.from(extensionBeanType).isPresent(EndpointCloudFoundryExtension.class);
} }
static class CloudFoundryWebEndpointDiscovererRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection()
.registerType(CloudFoundryEndpointFilter.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
}
}
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,12 +17,12 @@
package org.springframework.boot.actuate.autoconfigure.cloudfoundry; package org.springframework.boot.actuate.autoconfigure.cloudfoundry;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason;
import org.springframework.boot.json.JsonParserFactory; import org.springframework.boot.json.JsonParserFactory;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -60,7 +60,7 @@ public class Token {
private Map<String, Object> parseJson(String base64) { private Map<String, Object> parseJson(String base64) {
try { try {
byte[] bytes = Base64.getUrlDecoder().decode(base64); byte[] bytes = Base64Utils.decodeFromUrlSafeString(base64);
return JsonParserFactory.getJsonParser().parseMap(new String(bytes, StandardCharsets.UTF_8)); return JsonParserFactory.getJsonParser().parseMap(new String(bytes, StandardCharsets.UTF_8));
} }
catch (RuntimeException ex) { catch (RuntimeException ex) {
@ -73,7 +73,7 @@ public class Token {
} }
public byte[] getSignature() { public byte[] getSignature() {
return Base64.getUrlDecoder().decode(this.signature); return Base64Utils.decodeFromUrlSafeString(this.signature);
} }
public String getSignatureAlgorithm() { public String getSignatureAlgorithm() {

@ -93,7 +93,8 @@ class CloudFoundrySecurityInterceptor {
} }
private Mono<SecurityResponse> getErrorResponse(Throwable throwable) { private Mono<SecurityResponse> getErrorResponse(Throwable throwable) {
if (throwable instanceof CloudFoundryAuthorizationException cfException) { if (throwable instanceof CloudFoundryAuthorizationException) {
CloudFoundryAuthorizationException cfException = (CloudFoundryAuthorizationException) throwable;
return Mono.just(new SecurityResponse(cfException.getStatusCode(), return Mono.just(new SecurityResponse(cfException.getStatusCode(),
"{\"security_error\":\"" + cfException.getMessage() + "\"}")); "{\"security_error\":\"" + cfException.getMessage() + "\"}"));
} }

@ -25,14 +25,8 @@ import java.util.stream.Collectors;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import org.springframework.aot.hint.BindingReflectionHintsRegistrar;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.annotation.Reflective;
import org.springframework.aot.hint.annotation.ReflectiveRuntimeHintsRegistrar;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.reactive.CloudFoundryWebFluxEndpointHandlerMapping.CloudFoundryWebFluxEndpointHandlerMappingRuntimeHints;
import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint; import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
@ -42,7 +36,6 @@ import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.Link; import org.springframework.boot.actuate.endpoint.web.Link;
import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.actuate.endpoint.web.WebOperation;
import org.springframework.boot.actuate.endpoint.web.reactive.AbstractWebFluxEndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.web.reactive.AbstractWebFluxEndpointHandlerMapping;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
@ -58,7 +51,6 @@ import org.springframework.web.server.ServerWebExchange;
* @author Phillip Webb * @author Phillip Webb
* @author Brian Clozel * @author Brian Clozel
*/ */
@ImportRuntimeHints(CloudFoundryWebFluxEndpointHandlerMappingRuntimeHints.class)
class CloudFoundryWebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointHandlerMapping { class CloudFoundryWebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointHandlerMapping {
private final CloudFoundrySecurityInterceptor securityInterceptor; private final CloudFoundrySecurityInterceptor securityInterceptor;
@ -95,7 +87,6 @@ class CloudFoundryWebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointH
class CloudFoundryLinksHandler implements LinksHandler { class CloudFoundryLinksHandler implements LinksHandler {
@Override @Override
@Reflective
public Publisher<ResponseEntity<Object>> links(ServerWebExchange exchange) { public Publisher<ResponseEntity<Object>> links(ServerWebExchange exchange) {
ServerHttpRequest request = exchange.getRequest(); ServerHttpRequest request = exchange.getRequest();
return CloudFoundryWebFluxEndpointHandlerMapping.this.securityInterceptor.preHandle(exchange, "") return CloudFoundryWebFluxEndpointHandlerMapping.this.securityInterceptor.preHandle(exchange, "")
@ -162,18 +153,4 @@ class CloudFoundryWebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointH
} }
static class CloudFoundryWebFluxEndpointHandlerMappingRuntimeHints implements RuntimeHintsRegistrar {
private final ReflectiveRuntimeHintsRegistrar reflectiveRegistrar = new ReflectiveRuntimeHintsRegistrar();
private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar();
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
this.reflectiveRegistrar.registerRuntimeHints(hints, CloudFoundryLinksHandler.class);
this.bindingRegistrar.registerReflectionHints(hints.reflection(), Link.class);
}
}
} }

@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
@ -102,7 +103,7 @@ public class ReactiveCloudFoundryActuatorAutoConfiguration {
List<InfoContributor> contributors = infoContributors.orderedStream() List<InfoContributor> contributors = infoContributors.orderedStream()
.map((infoContributor) -> (infoContributor instanceof GitInfoContributor) .map((infoContributor) -> (infoContributor instanceof GitInfoContributor)
? new GitInfoContributor(properties, InfoPropertiesInfoContributor.Mode.FULL) : infoContributor) ? new GitInfoContributor(properties, InfoPropertiesInfoContributor.Mode.FULL) : infoContributor)
.toList(); .collect(Collectors.toList());
return new CloudFoundryInfoEndpointWebExtension(new InfoEndpoint(contributors)); return new CloudFoundryInfoEndpointWebExtension(new InfoEndpoint(contributors));
} }
@ -174,14 +175,14 @@ public class ReactiveCloudFoundryActuatorAutoConfiguration {
@Override @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebFilterChainProxy webFilterChainProxy) { if (bean instanceof WebFilterChainProxy) {
return postProcess(webFilterChainProxy); return postProcess((WebFilterChainProxy) bean, this.pathMappedEndpoints.get());
} }
return bean; return bean;
} }
private WebFilterChainProxy postProcess(WebFilterChainProxy existing) { private WebFilterChainProxy postProcess(WebFilterChainProxy existing, PathMappedEndpoints pathMappedEndpoints) {
List<String> paths = getPaths(this.pathMappedEndpoints.get()); List<String> paths = getPaths(pathMappedEndpoints);
ServerWebExchangeMatcher cloudFoundryRequestMatcher = ServerWebExchangeMatchers ServerWebExchangeMatcher cloudFoundryRequestMatcher = ServerWebExchangeMatchers
.pathMatchers(paths.toArray(new String[] {})); .pathMatchers(paths.toArray(new String[] {}));
WebFilter noOpFilter = (exchange, chain) -> chain.filter(exchange); WebFilter noOpFilter = (exchange, chain) -> chain.filter(exchange);

@ -31,7 +31,6 @@ import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryA
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason;
import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient;
@ -46,16 +45,18 @@ import org.springframework.web.reactive.function.client.WebClientResponseExcepti
*/ */
class ReactiveCloudFoundrySecurityService { class ReactiveCloudFoundrySecurityService {
private static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<>() { private static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<Map<String, Object>>() {
}; };
private final WebClient webClient; private final WebClient webClient;
private final String cloudControllerUrl; private final String cloudControllerUrl;
private Mono<String> uaaUrl;
ReactiveCloudFoundrySecurityService(WebClient.Builder webClientBuilder, String cloudControllerUrl, ReactiveCloudFoundrySecurityService(WebClient.Builder webClientBuilder, String cloudControllerUrl,
boolean skipSslValidation) { boolean skipSslValidation) {
Assert.notNull(webClientBuilder, "WebClient must not be null"); Assert.notNull(webClientBuilder, "Webclient must not be null");
Assert.notNull(cloudControllerUrl, "CloudControllerUrl must not be null"); Assert.notNull(cloudControllerUrl, "CloudControllerUrl must not be null");
if (skipSslValidation) { if (skipSslValidation) {
webClientBuilder.clientConnector(buildTrustAllSslConnector()); webClientBuilder.clientConnector(buildTrustAllSslConnector());
@ -94,8 +95,8 @@ class ReactiveCloudFoundrySecurityService {
} }
private Throwable mapError(Throwable throwable) { private Throwable mapError(Throwable throwable) {
if (throwable instanceof WebClientResponseException webClientResponseException) { if (throwable instanceof WebClientResponseException) {
HttpStatusCode statusCode = webClientResponseException.getStatusCode(); HttpStatus statusCode = ((WebClientResponseException) throwable).getStatusCode();
if (statusCode.equals(HttpStatus.FORBIDDEN)) { if (statusCode.equals(HttpStatus.FORBIDDEN)) {
return new CloudFoundryAuthorizationException(Reason.ACCESS_DENIED, "Access denied"); return new CloudFoundryAuthorizationException(Reason.ACCESS_DENIED, "Access denied");
} }
@ -147,7 +148,7 @@ class ReactiveCloudFoundrySecurityService {
* @return the UAA url Mono * @return the UAA url Mono
*/ */
Mono<String> getUaaUrl() { Mono<String> getUaaUrl() {
return this.webClient.get() this.uaaUrl = this.webClient.get()
.uri(this.cloudControllerUrl + "/info") .uri(this.cloudControllerUrl + "/info")
.retrieve() .retrieve()
.bodyToMono(Map.class) .bodyToMono(Map.class)
@ -155,6 +156,7 @@ class ReactiveCloudFoundrySecurityService {
.cache() .cache()
.onErrorMap((ex) -> new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE, .onErrorMap((ex) -> new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE,
"Unable to fetch token keys from UAA.")); "Unable to fetch token keys from UAA."));
return this.uaaUrl;
} }
} }

@ -23,7 +23,6 @@ import java.security.PublicKey;
import java.security.Signature; import java.security.Signature;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -34,6 +33,7 @@ import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.Token; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.Token;
import org.springframework.util.Base64Utils;
/** /**
* Validator used to ensure that a signed {@link Token} has not been tampered with. * Validator used to ensure that a signed {@link Token} has not been tampered with.
@ -112,7 +112,7 @@ class ReactiveTokenValidator {
key = key.replace("-----BEGIN PUBLIC KEY-----\n", ""); key = key.replace("-----BEGIN PUBLIC KEY-----\n", "");
key = key.replace("-----END PUBLIC KEY-----", ""); key = key.replace("-----END PUBLIC KEY-----", "");
key = key.trim().replace("\n", ""); key = key.trim().replace("\n", "");
byte[] bytes = Base64.getDecoder().decode(key); byte[] bytes = Base64Utils.decodeFromString(key);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
return KeyFactory.getInstance("RSA").generatePublic(keySpec); return KeyFactory.getInstance("RSA").generatePublic(keySpec);
} }

@ -21,6 +21,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryWebEndpointDiscoverer; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryWebEndpointDiscoverer;
@ -107,7 +108,7 @@ public class CloudFoundryActuatorAutoConfiguration {
List<InfoContributor> contributors = infoContributors.orderedStream() List<InfoContributor> contributors = infoContributors.orderedStream()
.map((infoContributor) -> (infoContributor instanceof GitInfoContributor) .map((infoContributor) -> (infoContributor instanceof GitInfoContributor)
? new GitInfoContributor(properties, InfoPropertiesInfoContributor.Mode.FULL) : infoContributor) ? new GitInfoContributor(properties, InfoPropertiesInfoContributor.Mode.FULL) : infoContributor)
.toList(); .collect(Collectors.toList());
return new CloudFoundryInfoEndpointWebExtension(new InfoEndpoint(contributors)); return new CloudFoundryInfoEndpointWebExtension(new InfoEndpoint(contributors));
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,7 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.cloudfoundry.servlet;
import java.util.Locale; import java.util.Locale;
import jakarta.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -77,7 +78,8 @@ class CloudFoundrySecurityInterceptor {
} }
catch (Exception ex) { catch (Exception ex) {
logger.error(ex); logger.error(ex);
if (ex instanceof CloudFoundryAuthorizationException cfException) { if (ex instanceof CloudFoundryAuthorizationException) {
CloudFoundryAuthorizationException cfException = (CloudFoundryAuthorizationException) ex;
return new SecurityResponse(cfException.getStatusCode(), return new SecurityResponse(cfException.getStatusCode(),
"{\"security_error\":\"" + cfException.getMessage() + "\"}"); "{\"security_error\":\"" + cfException.getMessage() + "\"}");
} }
@ -86,7 +88,7 @@ class CloudFoundrySecurityInterceptor {
return SecurityResponse.success(); return SecurityResponse.success();
} }
private void check(HttpServletRequest request, EndpointId endpointId) { private void check(HttpServletRequest request, EndpointId endpointId) throws Exception {
Token token = getToken(request); Token token = getToken(request);
this.tokenValidator.validate(token); this.tokenValidator.validate(token);
AccessLevel accessLevel = this.cloudFoundrySecurityService.getAccessLevel(token.toString(), this.applicationId); AccessLevel accessLevel = this.cloudFoundrySecurityService.getAccessLevel(token.toString(), this.applicationId);

@ -22,19 +22,14 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import jakarta.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.aot.hint.BindingReflectionHintsRegistrar;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.annotation.Reflective;
import org.springframework.aot.hint.annotation.ReflectiveRuntimeHintsRegistrar;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse; import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse;
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.servlet.CloudFoundryWebEndpointServletHandlerMapping.CloudFoundryWebEndpointServletHandlerMappingRuntimeHints;
import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint; import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
@ -44,7 +39,7 @@ import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.Link; import org.springframework.boot.actuate.endpoint.web.Link;
import org.springframework.boot.actuate.endpoint.web.WebOperation; import org.springframework.boot.actuate.endpoint.web.WebOperation;
import org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping;
import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@ -59,7 +54,6 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMappi
* @author Phillip Webb * @author Phillip Webb
* @author Brian Clozel * @author Brian Clozel
*/ */
@ImportRuntimeHints(CloudFoundryWebEndpointServletHandlerMappingRuntimeHints.class)
class CloudFoundryWebEndpointServletHandlerMapping extends AbstractWebMvcEndpointHandlerMapping { class CloudFoundryWebEndpointServletHandlerMapping extends AbstractWebMvcEndpointHandlerMapping {
private static final Log logger = LogFactory.getLog(CloudFoundryWebEndpointServletHandlerMapping.class); private static final Log logger = LogFactory.getLog(CloudFoundryWebEndpointServletHandlerMapping.class);
@ -74,7 +68,8 @@ class CloudFoundryWebEndpointServletHandlerMapping extends AbstractWebMvcEndpoin
Collection<ExposableWebEndpoint> endpoints, EndpointMediaTypes endpointMediaTypes, Collection<ExposableWebEndpoint> endpoints, EndpointMediaTypes endpointMediaTypes,
CorsConfiguration corsConfiguration, CloudFoundrySecurityInterceptor securityInterceptor, CorsConfiguration corsConfiguration, CloudFoundrySecurityInterceptor securityInterceptor,
Collection<ExposableEndpoint<?>> allEndpoints) { Collection<ExposableEndpoint<?>> allEndpoints) {
super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, true); super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, true,
WebMvcAutoConfiguration.pathPatternParser);
this.securityInterceptor = securityInterceptor; this.securityInterceptor = securityInterceptor;
this.linksResolver = new EndpointLinksResolver(allEndpoints); this.linksResolver = new EndpointLinksResolver(allEndpoints);
this.allEndpoints = allEndpoints; this.allEndpoints = allEndpoints;
@ -99,7 +94,6 @@ class CloudFoundryWebEndpointServletHandlerMapping extends AbstractWebMvcEndpoin
@Override @Override
@ResponseBody @ResponseBody
@Reflective
public Map<String, Map<String, Link>> links(HttpServletRequest request, HttpServletResponse response) { public Map<String, Map<String, Link>> links(HttpServletRequest request, HttpServletResponse response) {
SecurityResponse securityResponse = CloudFoundryWebEndpointServletHandlerMapping.this.securityInterceptor SecurityResponse securityResponse = CloudFoundryWebEndpointServletHandlerMapping.this.securityInterceptor
.preHandle(request, null); .preHandle(request, null);
@ -165,18 +159,4 @@ class CloudFoundryWebEndpointServletHandlerMapping extends AbstractWebMvcEndpoin
} }
static class CloudFoundryWebEndpointServletHandlerMappingRuntimeHints implements RuntimeHintsRegistrar {
private final ReflectiveRuntimeHintsRegistrar reflectiveRegistrar = new ReflectiveRuntimeHintsRegistrar();
private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar();
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
this.reflectiveRegistrar.registerRuntimeHints(hints, CloudFoundryLinksHandler.class);
this.bindingRegistrar.registerReflectionHints(hints.reflection(), Link.class);
}
}
} }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save