Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
325bfdb
Bump @antora/cli from 3.2.0-alpha.11 to 3.2.0-alpha.12 in /antora
dependabot[bot] May 19, 2026
cb06492
Merge pull request #50464 from dependabot[bot]
snicoll May 29, 2026
dd04d8b
Merge branch '3.5.x' into 4.0.x
snicoll May 29, 2026
435a837
Merge branch '4.0.x'
snicoll May 29, 2026
6074f06
Bump @antora/site-generator in /antora
dependabot[bot] May 29, 2026
b58a5b1
Merge pull request #50463 from dependabot[bot]
snicoll May 29, 2026
4b50058
Merge branch '3.5.x' into 4.0.x
snicoll May 29, 2026
ee869ec
Merge branch '4.0.x'
snicoll May 29, 2026
08194cf
Fall back to default SSL provider for unmapped SNI hostnames
kwondh5217 May 6, 2026
8221bdb
Polish "Fall back to default SSL provider for unmapped SNI hostnames"
snicoll May 29, 2026
7cf7d23
Upgrade to MongoDB 5.8.0
wilkinsona May 29, 2026
d33962f
Merge pull request #50301 from kwondh5217
snicoll May 29, 2026
e51e821
Merge branch '3.5.x' into 4.0.x
snicoll May 29, 2026
f281ff4
Merge branch '4.0.x'
snicoll May 29, 2026
cfc1e89
Treat empty RabbitMQ SSL bundle as unset
igormukhin May 13, 2026
19c7f5a
Merge pull request #50429 from igormukhin
snicoll May 29, 2026
170c52f
Merge branch '3.5.x' into 4.0.x
snicoll May 29, 2026
3638852
Merge branch '4.0.x'
snicoll May 29, 2026
3c5309b
Clarify dependency requirement for Bean Validation support
Kapil-chn7 May 4, 2026
091b0f3
Polish "Clarify dependency requirement for Bean Validation support"
snicoll May 29, 2026
351a826
Merge pull request #50290 from Kapil-chn7
snicoll May 29, 2026
78cdd41
Merge branch '3.5.x' into 4.0.x
snicoll May 29, 2026
8ff59ca
Merge branch '4.0.x'
snicoll May 29, 2026
155b02f
Use new Maven Resolver API for ModifiedClassPathClassLoader
nosan May 15, 2026
ae3222d
Polish "Use new Maven Resolver API for ModifiedClassPathClassLoader"
snicoll May 29, 2026
3e5ad73
Merge pull request #50447 from nosan
snicoll May 29, 2026
2e825ba
Merge branch '3.5.x' into 4.0.x
snicoll May 29, 2026
aa34fbb
Merge branch '4.0.x'
snicoll May 29, 2026
8a28cef
Restore interrupt flag in ProcessRunner on InterruptedException
SebTardif May 15, 2026
bb30ecc
Merge pull request #50451 from SebTardif
snicoll May 29, 2026
3209464
Merge branch '3.5.x' into 4.0.x
snicoll May 29, 2026
325b234
Merge branch '4.0.x'
snicoll May 29, 2026
8786f37
Fix typos in documentation
SJvaca30 May 27, 2026
a862a08
Polish "Fix typos in documentation"
snicoll May 29, 2026
6d44476
Merge pull request #50593 from SJvaca30
snicoll May 29, 2026
8845b31
Merge branch '3.5.x' into 4.0.x
snicoll May 29, 2026
6a625e2
Merge branch '4.0.x'
snicoll May 29, 2026
c0d4ebc
Remove the use of Optional from Data Neo4j repository examples
nosan May 28, 2026
30876aa
Merge pull request #50600 from nosan
snicoll May 29, 2026
85ca245
Merge branch '3.5.x' into 4.0.x
snicoll May 29, 2026
aef3aa2
Merge branch '4.0.x'
snicoll May 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
454 changes: 152 additions & 302 deletions antora/package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions antora/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"antora": "node npm/antora.js"
},
"dependencies": {
"@antora/cli": "3.2.0-alpha.11",
"@antora/site-generator": "3.2.0-alpha.11",
"@antora/cli": "3.2.0-alpha.12",
"@antora/site-generator": "3.2.0-alpha.12",
"@antora/atlas-extension": "1.0.0-alpha.5",
"@springio/antora-extensions": "1.14.11",
"@springio/antora-xref-extension": "1.0.0-alpha.5",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ To get started with the plugin it needs to be applied to your project.
ifeval::["{build-type}" == "commercial"]
The plugin is published to the Spring Commercial repository.
You will have to configure your build to access this repository.
This is usual done through a local artifact repository that mirrors the content of the Spring Commercial repository.
This is usually done through a local artifact repository that mirrors the content of the Spring Commercial repository.
Alternatively, while it is not recommended, the Spring Commercial repository can also be accessed directly.
In either case, see https://docs.vmware.com/en/Tanzu-Spring-Runtime/Commercial/Tanzu-Spring-Runtime/spring-enterprise-subscription.html[the Tanzu Spring Runtime documentation] for further details.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ private int waitForProcess(Process process) {
return process.waitFor();
}
catch (InterruptedException ex) {
throw new IllegalStateException("Interrupted waiting for %s".formatted(process));
Thread.currentThread().interrupt();
throw new IllegalStateException("Interrupted waiting for %s".formatted(process), ex);
}
}

Expand Down Expand Up @@ -190,6 +191,7 @@ public String toString() {
return this.output.toString();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
return "";
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
= Deprecated Application Properties

The following deprecated properties can be specified inside your `application.properties` file, inside your `application.yaml` file, or as command line switches.
Support for these properties will be removed in a future release and should you should migrate away from them.
Support for these properties will be removed in a future release and you should migrate away from them.

[TIP]
====
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[[io.validation]]
= Validation

The method validation feature supported by Bean Validation 1.1 is automatically enabled as long as a JSR-303 implementation (such as Hibernate validator) is on the classpath.
The method validation feature supported by Bean Validation 1.1 is automatically enabled as long as a JSR-303 implementation (such as Hibernate Validator, typically provided by `spring-boot-starter-validation`) is on the classpath.
This lets bean methods be annotated with `jakarta.validation` constraints on their parameters and/or on their return value.
Target classes with such annotated methods need to be annotated with the javadoc:org.springframework.validation.annotation.Validated[format=annotation] annotation at the type level for their methods to be searched for inline constraint annotations.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ endif::[]

ifeval::["{build-type}" == "commercial"]
You will also have to configure your build to access the Spring Commercial repository.
This is usual done through a local artifact repository that mirrors the content of the Spring Commercial repository.
This is usually done through a local artifact repository that mirrors the content of the Spring Commercial repository.
Alternatively, while it is not recommended, the Spring Commercial repository can also be accessed directly.
In either case, see https://docs.vmware.com/en/Tanzu-Spring-Runtime/Commercial/Tanzu-Spring-Runtime/spring-enterprise-subscription.html[the Tanzu Spring Runtime documentation] for further details.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@

package org.springframework.boot.docs.data.nosql.neo4j.repositories;

import java.util.Optional;

import org.springframework.data.neo4j.repository.Neo4jRepository;

public interface CityRepository extends Neo4jRepository<City, Long> {

Optional<City> findOneByNameAndState(String name, String state);
City findOneByNameAndState(String name, String state);

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@
package org.springframework.boot.docs.data.nosql.neo4j.repositories

import org.springframework.data.neo4j.repository.Neo4jRepository
import java.util.Optional

interface CityRepository : Neo4jRepository<City, Long> {

fun findOneByNameAndState(name: String?, state: String?): Optional<City?>?
fun findOneByNameAndState(name: String?, state: String?): City?

}

Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ public class Ssl {
* @see #getEnabled() ()
*/
public boolean determineEnabled() {
boolean defaultEnabled = Boolean.TRUE.equals(getEnabled()) || this.bundle != null;
boolean defaultEnabled = Boolean.TRUE.equals(getEnabled()) || StringUtils.hasText(this.bundle);
if (CollectionUtils.isEmpty(RabbitProperties.this.parsedAddresses)) {
return defaultEnabled;
}
Expand Down Expand Up @@ -1387,7 +1387,7 @@ public static class Ssl {
}

public boolean determineEnabled() {
return Boolean.TRUE.equals(getEnabled()) || this.bundle != null;
return Boolean.TRUE.equals(getEnabled()) || StringUtils.hasText(this.bundle);
}

public void setEnabled(@Nullable Boolean enabled) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,12 @@ void determineSslEnabledIsTrueWhenBundleIsSetAndNoAddresses() {
assertThat(this.properties.getSsl().determineEnabled()).isTrue();
}

@Test
void determineSslEnabledIsFalseWhenBundleIsEmpty() {
this.properties.getSsl().setBundle("");
assertThat(this.properties.getSsl().determineEnabled()).isFalse();
}

@Test
void propertiesUseConsistentDefaultValues() {
ConnectionFactory connectionFactory = new ConnectionFactory();
Expand Down Expand Up @@ -399,6 +405,12 @@ void streamSslIsEnabledWhenBundleIsSet() {
assertThat(this.properties.getStream().getSsl().determineEnabled()).isTrue();
}

@Test
void streamSslIsDisabledWhenBundleIsEmpty() {
this.properties.getStream().getSsl().setBundle("");
assertThat(this.properties.getStream().getSsl().determineEnabled()).isFalse();
}

@Test
void streamSslIsDisabledWhenEnabledIsFalseAndBundleIsNotSet() {
this.properties.getStream().getSsl().setEnabled(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,13 @@ public HttpServer apply(HttpServer server) {
}

private void applySecurity(SslContextSpec spec) {
spec.sslContext(this.sslProvider.getSslContext()).setSniAsyncMappings((serverName, promise) -> {
SslProvider provider = (serverName != null) ? this.serverNameSslProviders.get(serverName)
: this.sslProvider;
return promise.setSuccess(provider);
});
spec.sslContext(this.sslProvider.getSslContext())
.setSniAsyncMappings((serverName, promise) -> promise.setSuccess(getSslProvider(serverName)));
}

SslProvider getSslProvider(@Nullable String serverName) {
return (serverName != null) ? this.serverNameSslProviders.getOrDefault(serverName, this.sslProvider)
: this.sslProvider;
}

void updateSslBundle(@Nullable String serverName, SslBundle sslBundle) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2012-present 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.reactor.netty;

import java.util.Collections;
import java.util.Map;

import org.junit.jupiter.api.Test;
import reactor.netty.tcp.SslProvider;

import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.ssl.pem.PemSslStoreBundle;
import org.springframework.boot.ssl.pem.PemSslStoreDetails;
import org.springframework.boot.testsupport.classpath.resources.WithPackageResources;
import org.springframework.boot.web.server.Ssl;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for {@link SslServerCustomizer}.
*
* @author Daeho Kwon
*/
class SslServerCustomizerTests {

@Test
@WithPackageResources({ "1.key", "1.crt", "2.key", "2.crt" })
void getSslProviderReturnsMappedProviderForKnownServerName() {
SslBundle defaultBundle = createBundle("1.key", "1.crt");
SslBundle mappedBundle = createBundle("2.key", "2.crt");
SslServerCustomizer customizer = new SslServerCustomizer(null, Ssl.ClientAuth.NONE, defaultBundle,
Map.of("mapped.example", mappedBundle));
SslProvider mapped = customizer.getSslProvider("mapped.example");
assertThat(mapped).isNotNull().isNotSameAs(customizer.getSslProvider(null));
}

@Test
@WithPackageResources({ "1.key", "1.crt", "2.key", "2.crt" })
void getSslProviderFallsBackToDefaultWhenServerNameIsUnmapped() {
SslBundle defaultBundle = createBundle("1.key", "1.crt");
SslBundle mappedBundle = createBundle("2.key", "2.crt");
SslServerCustomizer customizer = new SslServerCustomizer(null, Ssl.ClientAuth.NONE, defaultBundle,
Map.of("mapped.example", mappedBundle));
assertThat(customizer.getSslProvider("unmapped.example")).isSameAs(customizer.getSslProvider(null));
}

@Test
@WithPackageResources({ "1.key", "1.crt" })
@SuppressWarnings("NullAway") // Test null check
void getSslProviderReturnsDefaultWhenServerNameIsNull() {
SslBundle defaultBundle = createBundle("1.key", "1.crt");
SslServerCustomizer customizer = new SslServerCustomizer(null, Ssl.ClientAuth.NONE, defaultBundle,
Collections.emptyMap());
assertThat(customizer.getSslProvider(null)).isNotNull();
}

private static SslBundle createBundle(String key, String certificate) {
return SslBundle.of(new PemSslStoreBundle(
new PemSslStoreDetails(null, "classpath:" + certificate, "classpath:" + key), null));
}

}
2 changes: 1 addition & 1 deletion platform/spring-boot-dependencies/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -1658,7 +1658,7 @@ bom {
releaseNotes("https://github.com/mockito/mockito/releases/tag/v{version}")
}
}
library("MongoDB", "5.7.0") {
library("MongoDB", "5.8.0") {
alignWith {
version {
of "org.mongodb:mongodb-driver-core"
Expand Down
3 changes: 2 additions & 1 deletion platform/spring-boot-internal-dependencies/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,14 @@ bom {
]
}
}
library("Maven Resolver", "1.9.23") {
library("Maven Resolver", "1.9.27") {
group("org.apache.maven.resolver") {
modules = [
"maven-resolver-api",
"maven-resolver-connector-basic",
"maven-resolver-impl",
"maven-resolver-spi",
"maven-resolver-supplier",
"maven-resolver-transport-file",
"maven-resolver-transport-http",
"maven-resolver-util"
Expand Down
7 changes: 1 addition & 6 deletions test-support/spring-boot-test-support/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,7 @@ dependencies {
compileOnly("org.springframework.data:spring-data-redis")

implementation("jakarta.inject:jakarta.inject-api")
implementation("org.apache.maven.resolver:maven-resolver-connector-basic")
implementation("org.apache.maven.resolver:maven-resolver-impl")
implementation("org.apache.maven:maven-resolver-provider") {
exclude(group: "javax.inject", module: "javax.inject")
}
implementation("org.apache.maven.resolver:maven-resolver-transport-http") {
implementation("org.apache.maven.resolver:maven-resolver-supplier") {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
Expand All @@ -42,16 +43,13 @@
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResult;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
import org.eclipse.aether.supplier.RepositorySystemSupplier;

import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
Expand Down Expand Up @@ -241,42 +239,49 @@ private static List<URL> getAdditionalUrls(List<MergedAnnotations> annotations)
}

private static List<URL> resolveCoordinates(String[] coordinates) {
Exception latestFailure = null;
RepositorySystem repositorySystem = createRepositorySystem();
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
session.setSystemProperties(System.getProperties());
LocalRepository localRepository = new LocalRepository(System.getProperty("user.home") + "/.m2/repository");
RemoteRepository remoteRepository = new RemoteRepository.Builder("central", "default",
"https://repo.maven.apache.org/maven2")
.build();
session.setLocalRepositoryManager(repositorySystem.newLocalRepositoryManager(session, localRepository));
for (int i = 0; i < MAX_RESOLUTION_ATTEMPTS; i++) {
CollectRequest collectRequest = new CollectRequest(null, Arrays.asList(remoteRepository));
collectRequest.setDependencies(createDependencies(coordinates));
DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, null);
try {
DependencyResult result = repositorySystem.resolveDependencies(session, dependencyRequest);
List<URL> resolvedArtifacts = new ArrayList<>();
for (ArtifactResult artifact : result.getArtifactResults()) {
resolvedArtifacts.add(artifact.getArtifact().getFile().toURI().toURL());
return doWithRepositorySystem((repositorySystem) -> {
Exception latestFailure = null;
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
session.setSystemProperties(System.getProperties());
LocalRepository localRepository = new LocalRepository(System.getProperty("user.home") + "/.m2/repository");
RemoteRepository remoteRepository = new RemoteRepository.Builder("central", "default",
"https://repo.maven.apache.org/maven2")
.build();
session.setLocalRepositoryManager(repositorySystem.newLocalRepositoryManager(session, localRepository));
for (int i = 0; i < MAX_RESOLUTION_ATTEMPTS; i++) {
CollectRequest collectRequest = new CollectRequest(null, Arrays.asList(remoteRepository));
collectRequest.setDependencies(createDependencies(coordinates));
DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, null);
try {
DependencyResult result = repositorySystem.resolveDependencies(session, dependencyRequest);
List<URL> resolvedArtifacts = new ArrayList<>();
for (ArtifactResult artifact : result.getArtifactResults()) {
resolvedArtifacts.add(artifact.getArtifact().getFile().toURI().toURL());
}
return resolvedArtifacts;
}
catch (Exception ex) {
latestFailure = ex;
}
return resolvedArtifacts;
}
throw new IllegalStateException("Resolution failed after " + MAX_RESOLUTION_ATTEMPTS + " attempts",
latestFailure);
});
}

private static <T> T doWithRepositorySystem(Function<RepositorySystem, T> repositorySystem) {
RepositorySystem rs = new RepositorySystemSupplier().get();
try {
return repositorySystem.apply(rs);
}
finally {
try {
rs.shutdown();
}
catch (Exception ex) {
latestFailure = ex;
// Ignore
}
}
throw new IllegalStateException("Resolution failed after " + MAX_RESOLUTION_ATTEMPTS + " attempts",
latestFailure);
}

@SuppressWarnings("deprecation")
private static RepositorySystem createRepositorySystem() {
org.eclipse.aether.impl.DefaultServiceLocator serviceLocator = MavenRepositorySystemUtils.newServiceLocator();
serviceLocator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
serviceLocator.addService(TransporterFactory.class, HttpTransporterFactory.class);
RepositorySystem repositorySystem = serviceLocator.getService(RepositorySystem.class);
return repositorySystem;
}

private static List<Dependency> createDependencies(String[] allCoordinates) {
Expand Down
Loading