@ -19,19 +19,25 @@ package org.springframework.boot.build.bom.bomr;
@@ -19,19 +19,25 @@ package org.springframework.boot.build.bom.bomr;
import java.util.ArrayList ;
import java.util.Collection ;
import java.util.Collections ;
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.LinkedHashMap ;
import java.util.List ;
import java.util.Map ;
import java.util.Set ;
import java.util.SortedSet ;
import java.util.stream.Collectors ;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion ;
import org.gradle.api.InvalidUserDataException ;
import org.gradle.api.internal.tasks.userinput.UserInputHandler ;
import org.springframework.boot.build.bom.Library ;
import org.springframework.boot.build.bom.Library.DependencyVersions ;
import org.springframework.boot.build.bom.Library.Group ;
import org.springframework.boot.build.bom.Library.Module ;
import org.springframework.boot.build.bom.Library.ProhibitedVersion ;
import org.springframework.boot.build.bom.Library.VersionAlignment ;
import org.springframework.boot.build.bom.UpgradePolicy ;
import org.springframework.boot.build.bom.bomr.version.DependencyVersion ;
import org.springframework.util.StringUtils ;
@ -59,39 +65,91 @@ public final class InteractiveUpgradeResolver implements UpgradeResolver {
@@ -59,39 +65,91 @@ public final class InteractiveUpgradeResolver implements UpgradeResolver {
@Override
public List < Upgrade > resolveUpgrades ( Collection < Library > libraries ) {
return libraries . stream ( ) . map ( this : : resolveUpgrade ) . filter ( ( upgrade ) - > upgrade ! = null )
. collect ( Collectors . toList ( ) ) ;
Map < String , Library > librariesByName = new HashMap < > ( ) ;
for ( Library library : libraries ) {
librariesByName . put ( library . getName ( ) , library ) ;
}
return libraries . stream ( ) . map ( ( library ) - > resolveUpgrade ( library , librariesByName ) )
. filter ( ( upgrade ) - > upgrade ! = null ) . collect ( Collectors . toList ( ) ) ;
}
private Upgrade resolveUpgrade ( Library library ) {
private Upgrade resolveUpgrade ( Library library , Map < String , Library > libraries ) {
List < VersionOption > versionOptions = getVersionOptions ( library , libraries ) ;
if ( versionOptions . isEmpty ( ) ) {
return null ;
}
VersionOption current = new VersionOption ( library . getVersion ( ) . getVersion ( ) ) ;
VersionOption selected = this . userInputHandler
. selectOption ( library . getName ( ) + " " + library . getVersion ( ) . getVersion ( ) , versionOptions , current ) ;
return ( selected . equals ( current ) ) ? null : new Upgrade ( library , selected . version ) ;
}
private List < VersionOption > getVersionOptions ( Library library , Map < String , Library > libraries ) {
if ( library . getVersion ( ) . getVersionAlignment ( ) ! = null ) {
VersionOption alignedVersionOption = alignedVersionOption ( library , libraries ) ;
if ( ! isPermitted ( alignedVersionOption . version , library . getProhibitedVersions ( ) ) ) {
throw new InvalidUserDataException ( "Version alignment failed. Version " + alignedVersionOption . version
+ " from " + library . getName ( ) + " is prohibited" ) ;
}
return Collections . singletonList ( alignedVersionOption ) ;
}
Map < String , SortedSet < DependencyVersion > > moduleVersions = new LinkedHashMap < > ( ) ;
DependencyVersion libraryVersion = library . getVersion ( ) . getVersion ( ) ;
for ( Group group : library . getGroups ( ) ) {
for ( Module module : group . getModules ( ) ) {
moduleVersions . put ( group . getId ( ) + ":" + module . getName ( ) ,
getLaterVersionsForModule ( group . getId ( ) , module . getName ( ) , library . getVersion ( ) ) ) ;
getLaterVersionsForModule ( group . getId ( ) , module . getName ( ) , libraryVersion ) ) ;
}
for ( String bom : group . getBoms ( ) ) {
moduleVersions . put ( group . getId ( ) + ":" + bom ,
getLaterVersionsForModule ( group . getId ( ) , bom , library . getVersion ( ) ) ) ;
getLaterVersionsForModule ( group . getId ( ) , bom , libraryVersion ) ) ;
}
for ( String plugin : group . getPlugins ( ) ) {
moduleVersions . put ( group . getId ( ) + ":" + plugin ,
getLaterVersionsForModule ( group . getId ( ) , plugin , library . getVersion ( ) ) ) ;
getLaterVersionsForModule ( group . getId ( ) , plugin , libraryVersion ) ) ;
}
}
List < DependencyVersion > allVersions = moduleVersions . values ( ) . stream ( ) . flatMap ( SortedSet : : stream ) . distinct ( )
. filter ( ( dependencyVersion ) - > isPermitted ( dependencyVersion , library . getProhibitedVersions ( ) ) )
. collect ( Collectors . toList ( ) ) ;
if ( allVersions . isEmpty ( ) ) {
return null ;
return Collections . emptyList ( ) ;
}
List < VersionOption > versionOptions = allVersions . stream ( )
. map ( ( version ) - > new VersionOption ( version , getMissingModules ( moduleVersions , version ) ) )
return allVersions . stream ( )
. map ( ( version ) - > new Resolved VersionOption( version , getMissingModules ( moduleVersions , version ) ) )
. collect ( Collectors . toList ( ) ) ;
VersionOption current = new VersionOption ( library . getVersion ( ) , Collections . emptyList ( ) ) ;
VersionOption selected = this . userInputHandler . selectOption ( library . getName ( ) + " " + library . getVersion ( ) ,
versionOptions , current ) ;
return ( selected . equals ( current ) ) ? null : new Upgrade ( library , selected . version ) ;
}
private VersionOption alignedVersionOption ( Library library , Map < String , Library > libraries ) {
VersionAlignment versionAlignment = library . getVersion ( ) . getVersionAlignment ( ) ;
Library alignmentLibrary = libraries . get ( versionAlignment . getLibraryName ( ) ) ;
DependencyVersions dependencyVersions = alignmentLibrary . getDependencyVersions ( ) ;
if ( dependencyVersions = = null ) {
throw new InvalidUserDataException ( "Cannot align with library '" + versionAlignment . getLibraryName ( )
+ "' as it does not define any dependency versions" ) ;
}
if ( ! dependencyVersions . available ( ) ) {
return null ;
}
Set < String > versions = new HashSet < > ( ) ;
for ( Group group : library . getGroups ( ) ) {
for ( Module module : group . getModules ( ) ) {
String version = dependencyVersions . getVersion ( group . getId ( ) , module . getName ( ) ) ;
if ( version ! = null ) {
versions . add ( version ) ;
}
}
}
if ( versions . isEmpty ( ) ) {
throw new InvalidUserDataException ( "Cannot align with library '" + versionAlignment . getLibraryName ( )
+ "' as its dependency versions do not include any of this library's modules" ) ;
}
if ( versions . size ( ) > 1 ) {
throw new InvalidUserDataException ( "Cannot align with library '" + versionAlignment . getLibraryName ( )
+ "' as it uses multiple different versions of this library's modules" ) ;
}
String requiredVersion = versions . iterator ( ) . next ( ) ;
return new AlignedVersionOption ( DependencyVersion . parse ( requiredVersion ) , alignmentLibrary ) ;
}
private boolean isPermitted ( DependencyVersion dependencyVersion , List < ProhibitedVersion > prohibitedVersions ) {
@ -125,23 +183,53 @@ public final class InteractiveUpgradeResolver implements UpgradeResolver {
@@ -125,23 +183,53 @@ public final class InteractiveUpgradeResolver implements UpgradeResolver {
return versions ;
}
private static final class VersionOption {
private static class VersionOption {
private final DependencyVersion version ;
protected VersionOption ( DependencyVersion version ) {
this . version = version ;
}
@Override
public String toString ( ) {
return this . version . toString ( ) ;
}
}
private static final class AlignedVersionOption extends VersionOption {
private final Library alignedWith ;
private AlignedVersionOption ( DependencyVersion version , Library alignedWith ) {
super ( version ) ;
this . alignedWith = alignedWith ;
}
@Override
public String toString ( ) {
return super . toString ( ) + " (aligned with " + this . alignedWith . getName ( ) + " "
+ this . alignedWith . getVersion ( ) . getVersion ( ) + ")" ;
}
}
private static final class ResolvedVersionOption extends VersionOption {
private final List < String > missingModules ;
private VersionOption ( DependencyVersion version , List < String > missingModules ) {
this . version = version ;
private Resolved VersionOption( DependencyVersion version , List < String > missingModules ) {
super ( version ) ;
this . missingModules = missingModules ;
}
@Override
public String toString ( ) {
if ( this . missingModules . isEmpty ( ) ) {
return this . version . toString ( ) ;
return super . toString ( ) ;
}
return this . version + " (some modules are missing: "
return super . toString ( ) + " (some modules are missing: "
+ StringUtils . collectionToDelimitedString ( this . missingModules , ", " ) + ")" ;
}