Skip to content

Commit 5b2cb79

Browse files
committed
Configure more version alignment for Spring Data dependencies
Closes gh-45698
1 parent f075744 commit 5b2cb79

File tree

3 files changed

+180
-8
lines changed

3 files changed

+180
-8
lines changed

buildSrc/src/main/java/org/springframework/boot/build/bom/BomExtension.java

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,17 @@
3838
import org.gradle.api.model.ObjectFactory;
3939
import org.gradle.api.plugins.JavaPlatformPlugin;
4040

41+
import org.springframework.boot.build.bom.BomExtension.LibraryHandler.AlignWithHandler.PropertyHandler;
42+
import org.springframework.boot.build.bom.BomExtension.LibraryHandler.AlignWithHandler.VersionHandler;
43+
import org.springframework.boot.build.bom.Library.DependencyVersionAlignment;
4144
import org.springframework.boot.build.bom.Library.Exclusion;
4245
import org.springframework.boot.build.bom.Library.Group;
4346
import org.springframework.boot.build.bom.Library.ImportedBom;
4447
import org.springframework.boot.build.bom.Library.LibraryVersion;
4548
import org.springframework.boot.build.bom.Library.Link;
4649
import org.springframework.boot.build.bom.Library.Module;
4750
import org.springframework.boot.build.bom.Library.PermittedDependency;
51+
import org.springframework.boot.build.bom.Library.PomPropertyVersionAlignment;
4852
import org.springframework.boot.build.bom.Library.ProhibitedVersion;
4953
import org.springframework.boot.build.bom.Library.VersionAlignment;
5054
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
@@ -106,16 +110,26 @@ public void library(String name, String version, Action<LibraryHandler> action)
106110
(version != null) ? version : "");
107111
action.execute(libraryHandler);
108112
LibraryVersion libraryVersion = new LibraryVersion(DependencyVersion.parse(libraryHandler.version));
109-
VersionAlignment versionAlignment = (libraryHandler.alignWith.version != null)
110-
? new VersionAlignment(libraryHandler.alignWith.version.from,
111-
libraryHandler.alignWith.version.managedBy, this.project, this.libraries, libraryHandler.groups)
112-
: null;
113113
addLibrary(new Library(name, libraryHandler.calendarName, libraryVersion, libraryHandler.groups,
114-
libraryHandler.prohibitedVersions, libraryHandler.considerSnapshots, versionAlignment,
114+
libraryHandler.prohibitedVersions, libraryHandler.considerSnapshots, versionAlignment(libraryHandler),
115115
libraryHandler.alignWith.dependencyManagementDeclaredIn, libraryHandler.linkRootName,
116116
libraryHandler.links));
117117
}
118118

119+
private VersionAlignment versionAlignment(LibraryHandler libraryHandler) {
120+
VersionHandler version = libraryHandler.alignWith.version;
121+
if (version != null) {
122+
return new DependencyVersionAlignment(version.from, version.managedBy, this.project, this.libraries,
123+
libraryHandler.groups);
124+
}
125+
PropertyHandler property = libraryHandler.alignWith.property;
126+
if (property != null) {
127+
return new PomPropertyVersionAlignment(property.name, property.of, property.managedBy, this.project,
128+
this.libraries);
129+
}
130+
return null;
131+
}
132+
119133
private String createDependencyNotation(String groupId, String artifactId, DependencyVersion version) {
120134
return groupId + ":" + artifactId + ":" + version;
121135
}
@@ -383,13 +397,20 @@ public static class AlignWithHandler {
383397

384398
private VersionHandler version;
385399

400+
private PropertyHandler property;
401+
386402
private String dependencyManagementDeclaredIn;
387403

388404
public void version(Action<VersionHandler> action) {
389405
this.version = new VersionHandler();
390406
action.execute(this.version);
391407
}
392408

409+
public void property(Action<PropertyHandler> action) {
410+
this.property = new PropertyHandler();
411+
action.execute(this.property);
412+
}
413+
393414
public void dependencyManagementDeclaredIn(String bomCoordinates) {
394415
this.dependencyManagementDeclaredIn = bomCoordinates;
395416
}
@@ -410,6 +431,28 @@ public void managedBy(String managedBy) {
410431

411432
}
412433

434+
public static class PropertyHandler {
435+
436+
private String name;
437+
438+
private String of;
439+
440+
private String managedBy;
441+
442+
public void name(String name) {
443+
this.name = name;
444+
}
445+
446+
public void of(String dependency) {
447+
this.of = dependency;
448+
}
449+
450+
public void managedBy(String managedBy) {
451+
this.managedBy = managedBy;
452+
}
453+
454+
}
455+
413456
}
414457

415458
}

buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.build.bom;
1818

19+
import java.io.File;
1920
import java.util.ArrayList;
2021
import java.util.Arrays;
2122
import java.util.Collections;
@@ -31,13 +32,19 @@
3132
import java.util.regex.Pattern;
3233
import java.util.stream.Stream;
3334

35+
import javax.xml.parsers.DocumentBuilder;
36+
import javax.xml.parsers.DocumentBuilderFactory;
37+
import javax.xml.xpath.XPath;
38+
import javax.xml.xpath.XPathFactory;
39+
3440
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
3541
import org.apache.maven.artifact.versioning.VersionRange;
3642
import org.gradle.api.Project;
3743
import org.gradle.api.artifacts.Configuration;
3844
import org.gradle.api.artifacts.Dependency;
3945
import org.gradle.api.artifacts.result.DependencyResult;
4046
import org.gradle.api.artifacts.result.ResolutionResult;
47+
import org.w3c.dom.Document;
4148

4249
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
4350

@@ -406,10 +413,16 @@ public String getArtifactId() {
406413

407414
}
408415

416+
public interface VersionAlignment {
417+
418+
Set<String> resolve();
419+
420+
}
421+
409422
/**
410-
* Version alignment for a library.
423+
* Version alignment for a library based on a dependency of another module.
411424
*/
412-
public static class VersionAlignment {
425+
public static class DependencyVersionAlignment implements VersionAlignment {
413426

414427
private final String from;
415428

@@ -423,14 +436,16 @@ public static class VersionAlignment {
423436

424437
private Set<String> alignedVersions;
425438

426-
VersionAlignment(String from, String managedBy, Project project, List<Library> libraries, List<Group> groups) {
439+
DependencyVersionAlignment(String from, String managedBy, Project project, List<Library> libraries,
440+
List<Group> groups) {
427441
this.from = from;
428442
this.managedBy = managedBy;
429443
this.project = project;
430444
this.libraries = libraries;
431445
this.groups = groups;
432446
}
433447

448+
@Override
434449
public Set<String> resolve() {
435450
if (this.alignedVersions != null) {
436451
return this.alignedVersions;
@@ -539,6 +554,100 @@ public String toString() {
539554

540555
}
541556

557+
/**
558+
* Version alignment for a library based on a property in the pom of another module.
559+
*/
560+
public static class PomPropertyVersionAlignment implements VersionAlignment {
561+
562+
private final String name;
563+
564+
private final String from;
565+
566+
private final String managedBy;
567+
568+
private final Project project;
569+
570+
private final List<Library> libraries;
571+
572+
private Set<String> alignedVersions;
573+
574+
PomPropertyVersionAlignment(String name, String from, String managedBy, Project project,
575+
List<Library> libraries) {
576+
this.name = name;
577+
this.from = from;
578+
this.managedBy = managedBy;
579+
this.project = project;
580+
this.libraries = libraries;
581+
}
582+
583+
@Override
584+
public Set<String> resolve() {
585+
if (this.alignedVersions != null) {
586+
return this.alignedVersions;
587+
}
588+
Configuration alignmentConfiguration = this.project.getConfigurations()
589+
.detachedConfiguration(getAligningDependencies().toArray(new Dependency[0]));
590+
Set<File> files = alignmentConfiguration.resolve();
591+
if (files.size() != 1) {
592+
throw new IllegalStateException(
593+
"Expected a single file when resolving the pom of " + this.from + " but found " + files.size());
594+
}
595+
File pomFile = files.iterator().next();
596+
return Set.of(propertyFrom(pomFile));
597+
}
598+
599+
private List<Dependency> getAligningDependencies() {
600+
Library managingLibrary = findManagingLibrary();
601+
List<Dependency> boms = getBomDependencies(managingLibrary);
602+
List<Dependency> dependencies = new ArrayList<>();
603+
dependencies.addAll(boms);
604+
dependencies.add(this.project.getDependencies().create(this.from + "@pom"));
605+
return dependencies;
606+
}
607+
608+
private Library findManagingLibrary() {
609+
if (this.managedBy == null) {
610+
return null;
611+
}
612+
return this.libraries.stream()
613+
.filter((candidate) -> this.managedBy.equals(candidate.getName()))
614+
.findFirst()
615+
.orElseThrow(() -> new IllegalStateException("Managing library '" + this.managedBy + "' not found."));
616+
}
617+
618+
private List<Dependency> getBomDependencies(Library manager) {
619+
return manager.getGroups()
620+
.stream()
621+
.flatMap((group) -> group.getBoms()
622+
.stream()
623+
.map((bom) -> this.project.getDependencies()
624+
.platform(group.getId() + ":" + bom.name() + ":" + manager.getVersion().getVersion())))
625+
.toList();
626+
}
627+
628+
private String propertyFrom(File pomFile) {
629+
try {
630+
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
631+
Document document = documentBuilder.parse(pomFile);
632+
XPath xpath = XPathFactory.newInstance().newXPath();
633+
return xpath.evaluate("/project/properties/" + this.name + "/text()", document);
634+
}
635+
catch (Exception ex) {
636+
throw new RuntimeException(ex);
637+
}
638+
}
639+
640+
@Override
641+
public String toString() {
642+
String result = "version from properties of " + this.from;
643+
if (this.managedBy != null) {
644+
result += " that is managed by " + this.managedBy;
645+
}
646+
return result;
647+
}
648+
649+
}
650+
542651
public record Link(String rootName, Function<LibraryVersion, String> factory, List<String> packages) {
543652

544653
private static final Pattern PACKAGE_EXPAND = Pattern.compile("^(.*)\\[(.*)\\]$");

spring-boot-project/spring-boot-dependencies/build.gradle

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,13 @@ bom {
939939
}
940940
}
941941
library("Jedis", "5.0.2") {
942+
alignWith {
943+
property {
944+
name "jedis"
945+
of "org.springframework.data:spring-data-redis"
946+
managedBy "Spring Data Bom"
947+
}
948+
}
942949
group("redis.clients") {
943950
modules = [
944951
"jedis"
@@ -1150,6 +1157,13 @@ bom {
11501157
}
11511158
}
11521159
library("Lettuce", "6.3.2.RELEASE") {
1160+
alignWith {
1161+
property {
1162+
name "lettuce"
1163+
of "org.springframework.data:spring-data-redis"
1164+
managedBy "Spring Data Bom"
1165+
}
1166+
}
11531167
group("io.lettuce") {
11541168
modules = [
11551169
"lettuce-core"
@@ -1452,6 +1466,12 @@ bom {
14521466
}
14531467
}
14541468
library("MongoDB", "5.0.1") {
1469+
alignWith {
1470+
version {
1471+
from "org.springframework.data:spring-data-mongodb"
1472+
managedBy "Spring Data Bom"
1473+
}
1474+
}
14551475
group("org.mongodb") {
14561476
modules = [
14571477
"bson",

0 commit comments

Comments
 (0)