Skip to content

Commit 8221de2

Browse files
committed
Merge branch '3.2.x'
Closes gh-40533
2 parents a12e3d4 + eb7e7b6 commit 8221de2

File tree

8 files changed

+230
-29
lines changed

8 files changed

+230
-29
lines changed

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

+37-18
Original file line numberDiff line numberDiff line change
@@ -115,16 +115,18 @@ public void library(String name, Action<LibraryHandler> action) {
115115

116116
public void library(String name, String version, Action<LibraryHandler> action) {
117117
ObjectFactory objects = this.project.getObjects();
118-
LibraryHandler libraryHandler = objects.newInstance(LibraryHandler.class, (version != null) ? version : "");
118+
LibraryHandler libraryHandler = objects.newInstance(LibraryHandler.class, this.project,
119+
(version != null) ? version : "");
119120
action.execute(libraryHandler);
120121
LibraryVersion libraryVersion = new LibraryVersion(DependencyVersion.parse(libraryHandler.version));
121-
VersionAlignment versionAlignment = (libraryHandler.alignWithVersion != null)
122-
? new VersionAlignment(libraryHandler.alignWithVersion.from, libraryHandler.alignWithVersion.managedBy,
123-
this.project, this.libraries, libraryHandler.groups)
122+
VersionAlignment versionAlignment = (libraryHandler.alignWith.version != null)
123+
? new VersionAlignment(libraryHandler.alignWith.version.from,
124+
libraryHandler.alignWith.version.managedBy, this.project, this.libraries, libraryHandler.groups)
124125
: null;
125126
addLibrary(new Library(name, libraryHandler.calendarName, libraryVersion, libraryHandler.groups,
126127
libraryHandler.prohibitedVersions, libraryHandler.considerSnapshots, versionAlignment,
127-
libraryHandler.linkRootName, libraryHandler.links));
128+
libraryHandler.alignWith.dependencyManagementDeclaredIn, libraryHandler.linkRootName,
129+
libraryHandler.links));
128130
}
129131

130132
public void effectiveBomArtifact() {
@@ -224,21 +226,22 @@ public static class LibraryHandler {
224226

225227
private final List<ProhibitedVersion> prohibitedVersions = new ArrayList<>();
226228

229+
private final AlignWithHandler alignWith;
230+
227231
private boolean considerSnapshots = false;
228232

229233
private String version;
230234

231235
private String calendarName;
232236

233-
private AlignWithVersionHandler alignWithVersion;
234-
235237
private String linkRootName;
236238

237239
private final Map<String, Function<LibraryVersion, String>> links = new HashMap<>();
238240

239241
@Inject
240-
public LibraryHandler(String version) {
242+
public LibraryHandler(Project project, String version) {
241243
this.version = version;
244+
this.alignWith = project.getObjects().newInstance(AlignWithHandler.class);
242245
}
243246

244247
public void version(String version) {
@@ -267,9 +270,8 @@ public void prohibit(Action<ProhibitedHandler> action) {
267270
handler.endsWith, handler.contains, handler.reason));
268271
}
269272

270-
public void alignWithVersion(Action<AlignWithVersionHandler> action) {
271-
this.alignWithVersion = new AlignWithVersionHandler();
272-
action.execute(this.alignWithVersion);
273+
public void alignWith(Action<AlignWithHandler> action) {
274+
action.execute(this.alignWith);
273275
}
274276

275277
public void links(Action<LinksHandler> action) {
@@ -400,18 +402,35 @@ public void setClassifier(String classifier) {
400402

401403
}
402404

403-
public static class AlignWithVersionHandler {
405+
public static class AlignWithHandler {
406+
407+
private VersionHandler version;
404408

405-
private String from;
409+
private String dependencyManagementDeclaredIn;
406410

407-
private String managedBy;
411+
public void version(Action<VersionHandler> action) {
412+
this.version = new VersionHandler();
413+
action.execute(this.version);
414+
}
408415

409-
public void from(String from) {
410-
this.from = from;
416+
public void dependencyManagementDeclaredIn(String bomCoordinates) {
417+
this.dependencyManagementDeclaredIn = bomCoordinates;
411418
}
412419

413-
public void managedBy(String managedBy) {
414-
this.managedBy = managedBy;
420+
public static class VersionHandler {
421+
422+
private String from;
423+
424+
private String managedBy;
425+
426+
public void from(String from) {
427+
this.from = from;
428+
}
429+
430+
public void managedBy(String managedBy) {
431+
this.managedBy = managedBy;
432+
}
433+
415434
}
416435

417436
}

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

+39
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.List;
2122
import java.util.Set;
@@ -36,6 +37,7 @@
3637
import org.springframework.boot.build.bom.Library.Module;
3738
import org.springframework.boot.build.bom.Library.ProhibitedVersion;
3839
import org.springframework.boot.build.bom.Library.VersionAlignment;
40+
import org.springframework.boot.build.bom.ManagedDependencies.Difference;
3941
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
4042

4143
/**
@@ -71,6 +73,7 @@ private void checkLibrary(Library library, List<String> errors) {
7173
checkExclusions(library, libraryErrors);
7274
checkProhibitedVersions(library, libraryErrors);
7375
checkVersionAlignment(library, libraryErrors);
76+
checkDependencyManagementAlignment(library, libraryErrors);
7477
if (!libraryErrors.isEmpty()) {
7578
errors.add(library.getName());
7679
for (String libraryError : libraryErrors) {
@@ -174,4 +177,40 @@ private void checkVersionAlignment(Library library, List<String> errors) {
174177
}
175178
}
176179

180+
private void checkDependencyManagementAlignment(Library library, List<String> errors) {
181+
String alignsWithBom = library.getAlignsWithBom();
182+
if (alignsWithBom == null) {
183+
return;
184+
}
185+
File bom = resolveBom(library, alignsWithBom);
186+
ManagedDependencies managedByBom = ManagedDependencies.ofBom(bom);
187+
ManagedDependencies managedByLibrary = ManagedDependencies.ofLibrary(library);
188+
Difference diff = managedByBom.diff(managedByLibrary);
189+
if (!diff.isEmpty()) {
190+
String error = "Dependency management does not align with " + library.getAlignsWithBom() + ":";
191+
if (!diff.missing().isEmpty()) {
192+
error = error + "%n - Missing:%n %s"
193+
.formatted(String.join("\n ", diff.missing()));
194+
}
195+
if (!diff.unexpected().isEmpty()) {
196+
error = error + "%n - Unexpected:%n %s"
197+
.formatted(String.join("\n ", diff.unexpected()));
198+
}
199+
errors.add(error);
200+
}
201+
}
202+
203+
private File resolveBom(Library library, String alignsWithBom) {
204+
String coordinates = alignsWithBom + ":" + library.getVersion().getVersion() + "@pom";
205+
Set<File> files = getProject().getConfigurations()
206+
.detachedConfiguration(getProject().getDependencies().create(coordinates))
207+
.getResolvedConfiguration()
208+
.getFiles();
209+
if (files.size() != 1) {
210+
throw new IllegalStateException(
211+
"Expected a single file but '" + coordinates + "' resolved to " + files.size());
212+
}
213+
return files.iterator().next();
214+
}
215+
177216
}

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

+10-1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ public class Library {
6262

6363
private final VersionAlignment versionAlignment;
6464

65+
private final String alignsWithBom;
66+
6567
private final String linkRootName;
6668

6769
private final Map<String, Function<LibraryVersion, String>> links;
@@ -77,13 +79,15 @@ public class Library {
7779
* @param prohibitedVersions version of the library that are prohibited
7880
* @param considerSnapshots whether to consider snapshots
7981
* @param versionAlignment version alignment, if any, for the library
82+
* @param alignsWithBom the coordinates of the bom, if any, that this library should
83+
* align with
8084
* @param linkRootName the root name to use when generating link variable or
8185
* {@code null} to generate one based on the library {@code name}
8286
* @param links a list of HTTP links relevant to the library
8387
*/
8488
public Library(String name, String calendarName, LibraryVersion version, List<Group> groups,
8589
List<ProhibitedVersion> prohibitedVersions, boolean considerSnapshots, VersionAlignment versionAlignment,
86-
String linkRootName, Map<String, Function<LibraryVersion, String>> links) {
90+
String alignsWithBom, String linkRootName, Map<String, Function<LibraryVersion, String>> links) {
8791
this.name = name;
8892
this.calendarName = (calendarName != null) ? calendarName : name;
8993
this.version = version;
@@ -93,6 +97,7 @@ public Library(String name, String calendarName, LibraryVersion version, List<Gr
9397
this.prohibitedVersions = prohibitedVersions;
9498
this.considerSnapshots = considerSnapshots;
9599
this.versionAlignment = versionAlignment;
100+
this.alignsWithBom = alignsWithBom;
96101
this.linkRootName = (linkRootName != null) ? linkRootName : generateLinkRootName(name);
97102
this.links = Collections.unmodifiableMap(links);
98103
}
@@ -137,6 +142,10 @@ public String getLinkRootName() {
137142
return this.linkRootName;
138143
}
139144

145+
public String getAlignsWithBom() {
146+
return this.alignsWithBom;
147+
}
148+
140149
public Map<String, String> getLinks() {
141150
Map<String, String> links = new TreeMap<>();
142151
this.links.forEach((name, linkFactory) -> links.put(name, linkFactory.apply(this.version)));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.build.bom;
18+
19+
import java.io.File;
20+
import java.io.FileReader;
21+
import java.util.HashMap;
22+
import java.util.HashSet;
23+
import java.util.Map;
24+
import java.util.Set;
25+
26+
import javax.xml.parsers.DocumentBuilderFactory;
27+
import javax.xml.xpath.XPath;
28+
import javax.xml.xpath.XPathConstants;
29+
import javax.xml.xpath.XPathFactory;
30+
31+
import org.w3c.dom.Document;
32+
import org.w3c.dom.Node;
33+
import org.w3c.dom.NodeList;
34+
import org.xml.sax.InputSource;
35+
36+
import org.springframework.boot.build.bom.Library.Group;
37+
import org.springframework.boot.build.bom.Library.Module;
38+
39+
/**
40+
* Managed dependencies from a bom or library.
41+
*
42+
* @author Andy Wilkinson
43+
*/
44+
class ManagedDependencies {
45+
46+
private final Set<String> ids;
47+
48+
ManagedDependencies(Set<String> ids) {
49+
this.ids = ids;
50+
}
51+
52+
Set<String> getIds() {
53+
return this.ids;
54+
}
55+
56+
Difference diff(ManagedDependencies other) {
57+
Set<String> missing = new HashSet<>(this.ids);
58+
missing.removeAll(other.ids);
59+
Set<String> unexpected = new HashSet<>(other.ids);
60+
unexpected.removeAll(this.ids);
61+
return new Difference(missing, unexpected);
62+
}
63+
64+
static ManagedDependencies ofBom(File bom) {
65+
try {
66+
Document bomDocument = DocumentBuilderFactory.newInstance()
67+
.newDocumentBuilder()
68+
.parse(new InputSource(new FileReader(bom)));
69+
XPath xpath = XPathFactory.newInstance().newXPath();
70+
NodeList dependencyNodes = (NodeList) xpath
71+
.evaluate("/project/dependencyManagement/dependencies/dependency", bomDocument, XPathConstants.NODESET);
72+
NodeList propertyNodes = (NodeList) xpath.evaluate("/project/properties/*", bomDocument,
73+
XPathConstants.NODESET);
74+
Map<String, String> properties = new HashMap<>();
75+
for (int i = 0; i < propertyNodes.getLength(); i++) {
76+
Node property = propertyNodes.item(i);
77+
String name = property.getNodeName();
78+
String value = property.getTextContent();
79+
properties.put("${%s}".formatted(name), value);
80+
}
81+
Set<String> managedDependencies = new HashSet<>();
82+
for (int i = 0; i < dependencyNodes.getLength(); i++) {
83+
Node dependency = dependencyNodes.item(i);
84+
String groupId = (String) xpath.evaluate("groupId/text()", dependency, XPathConstants.STRING);
85+
String artifactId = (String) xpath.evaluate("artifactId/text()", dependency, XPathConstants.STRING);
86+
String version = (String) xpath.evaluate("version/text()", dependency, XPathConstants.STRING);
87+
String classifier = (String) xpath.evaluate("classifier/text()", dependency, XPathConstants.STRING);
88+
if (version.startsWith("${") && version.endsWith("}")) {
89+
version = properties.get(version);
90+
}
91+
managedDependencies.add(asId(groupId, artifactId, version, classifier));
92+
}
93+
return new ManagedDependencies(managedDependencies);
94+
}
95+
catch (Exception ex) {
96+
throw new RuntimeException(ex);
97+
}
98+
}
99+
100+
static String asId(String groupId, String artifactId, String version, String classifier) {
101+
String id = groupId + ":" + artifactId + ":" + version;
102+
if (classifier != null && classifier.length() > 0) {
103+
id = id + ":" + classifier;
104+
}
105+
return id;
106+
}
107+
108+
static ManagedDependencies ofLibrary(Library library) {
109+
Set<String> managedByLibrary = new HashSet<>();
110+
for (Group group : library.getGroups()) {
111+
for (Module module : group.getModules()) {
112+
managedByLibrary.add(asId(group.getId(), module.getName(), library.getVersion().getVersion().toString(),
113+
module.getClassifier()));
114+
}
115+
}
116+
return new ManagedDependencies(managedByLibrary);
117+
}
118+
119+
record Difference(Set<String> missing, Set<String> unexpected) {
120+
121+
boolean isEmpty() {
122+
return this.missing.isEmpty() && this.unexpected.isEmpty();
123+
}
124+
125+
}
126+
127+
}

buildSrc/src/test/java/org/springframework/boot/build/antora/AntoraAsciidocAttributesTests.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,10 @@ private Library mockLibrary(Map<String, Function<LibraryVersion, String>> links)
144144
List<ProhibitedVersion> prohibitedVersion = Collections.emptyList();
145145
boolean considerSnapshots = false;
146146
VersionAlignment versionAlignment = null;
147+
String alignsWithBom = null;
147148
String linkRootName = null;
148149
Library library = new Library(name, calendarName, version, groups, prohibitedVersion, considerSnapshots,
149-
versionAlignment, linkRootName, links);
150+
versionAlignment, alignsWithBom, linkRootName, links);
150151
return library;
151152
}
152153

buildSrc/src/test/java/org/springframework/boot/build/bom/LibraryTests.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,11 @@ void getLinkRootNameWhenNoneSpecified() {
4747
List<ProhibitedVersion> prohibitedVersion = Collections.emptyList();
4848
boolean considerSnapshots = false;
4949
VersionAlignment versionAlignment = null;
50+
String alignsWithBom = null;
5051
String linkRootName = null;
5152
Map<String, Function<LibraryVersion, String>> links = Collections.emptyMap();
5253
Library library = new Library(name, calendarName, version, groups, prohibitedVersion, considerSnapshots,
53-
versionAlignment, linkRootName, links);
54+
versionAlignment, alignsWithBom, linkRootName, links);
5455
assertThat(library.getLinkRootName()).isEqualTo("spring-framework");
5556
}
5657

@@ -63,10 +64,11 @@ void getLinkRootNameWhenSpecified() {
6364
List<ProhibitedVersion> prohibitedVersion = Collections.emptyList();
6465
boolean considerSnapshots = false;
6566
VersionAlignment versionAlignment = null;
67+
String alignsWithBom = null;
6668
String linkRootName = "spring-data";
6769
Map<String, Function<LibraryVersion, String>> links = Collections.emptyMap();
6870
Library library = new Library(name, calendarName, version, groups, prohibitedVersion, considerSnapshots,
69-
versionAlignment, linkRootName, links);
71+
versionAlignment, alignsWithBom, linkRootName, links);
7072
assertThat(library.getLinkRootName()).isEqualTo("spring-data");
7173
}
7274

0 commit comments

Comments
 (0)