1616
1717package org .springframework .boot .build .bom ;
1818
19+ import java .io .File ;
1920import java .util .ArrayList ;
2021import java .util .Arrays ;
2122import java .util .Collections ;
3132import java .util .regex .Pattern ;
3233import 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+
3440import org .apache .maven .artifact .versioning .DefaultArtifactVersion ;
3541import org .apache .maven .artifact .versioning .VersionRange ;
3642import org .gradle .api .Project ;
3743import org .gradle .api .artifacts .Configuration ;
3844import org .gradle .api .artifacts .Dependency ;
3945import org .gradle .api .artifacts .result .DependencyResult ;
4046import org .gradle .api .artifacts .result .ResolutionResult ;
47+ import org .w3c .dom .Document ;
4148
4249import 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 ("^(.*)\\ [(.*)\\ ]$" );
0 commit comments