1717package org .springframework .boot .build .bom ;
1818
1919import java .util .ArrayList ;
20+ import java .util .Collections ;
21+ import java .util .HashMap ;
22+ import java .util .LinkedHashMap ;
2023import java .util .List ;
24+ import java .util .Map ;
2125import java .util .Optional ;
2226import java .util .Set ;
2327import java .util .TreeSet ;
4246import org .gradle .api .tasks .TaskAction ;
4347
4448import org .springframework .boot .build .bom .Library .Group ;
49+ import org .springframework .boot .build .bom .Library .ImportedBom ;
4550import org .springframework .boot .build .bom .Library .Module ;
51+ import org .springframework .boot .build .bom .Library .PermittedDependency ;
4652import org .springframework .boot .build .bom .Library .ProhibitedVersion ;
4753import org .springframework .boot .build .bom .Library .VersionAlignment ;
4854import org .springframework .boot .build .bom .ResolvedBom .Bom ;
@@ -69,7 +75,8 @@ public CheckBom(BomExtension bom) {
6975 Provider <ResolvedBom > resolvedBom = getResolvedBomFile ().map (RegularFile ::getAsFile ).map (ResolvedBom ::readFrom );
7076 this .checks = List .of (new CheckExclusions (configurations , dependencies ), new CheckProhibitedVersions (),
7177 new CheckVersionAlignment (),
72- new CheckDependencyManagementAlignment (resolvedBom , configurations , dependencies ));
78+ new CheckDependencyManagementAlignment (resolvedBom , configurations , dependencies ),
79+ new CheckForUnwantedDependencyManagement (resolvedBom ));
7380 this .bom = bom ;
7481 }
7582
@@ -241,31 +248,22 @@ private void check(VersionAlignment versionAlignment, Library library, List<Stri
241248
242249 }
243250
244- private static final class CheckDependencyManagementAlignment implements LibraryCheck {
251+ private abstract static class ResolvedLibraryCheck implements LibraryCheck {
245252
246253 private final Provider <ResolvedBom > resolvedBom ;
247254
248- private final BomResolver bomResolver ;
249-
250- private CheckDependencyManagementAlignment (Provider <ResolvedBom > resolvedBom ,
251- ConfigurationContainer configurations , DependencyHandler dependencies ) {
255+ private ResolvedLibraryCheck (Provider <ResolvedBom > resolvedBom ) {
252256 this .resolvedBom = resolvedBom ;
253- this .bomResolver = new BomResolver (configurations , dependencies );
254257 }
255258
256259 @ Override
257260 public List <String > check (Library library ) {
258- List <String > errors = new ArrayList <>();
259- String alignsWithBom = library .getAlignsWithBom ();
260- if (alignsWithBom != null ) {
261- Bom mavenBom = this .bomResolver
262- .resolveMavenBom (alignsWithBom + ":" + library .getVersion ().getVersion ());
263- ResolvedLibrary resolvedLibrary = getResolvedLibrary (library );
264- checkDependencyManagementAlignment (resolvedLibrary , mavenBom , errors );
265- }
266- return errors ;
261+ ResolvedLibrary resolvedLibrary = getResolvedLibrary (library );
262+ return check (library , resolvedLibrary );
267263 }
268264
265+ protected abstract List <String > check (Library library , ResolvedLibrary resolvedLibrary );
266+
269267 private ResolvedLibrary getResolvedLibrary (Library library ) {
270268 ResolvedBom resolvedBom = this .resolvedBom .get ();
271269 Optional <ResolvedLibrary > resolvedLibrary = resolvedBom .libraries ()
@@ -278,6 +276,30 @@ private ResolvedLibrary getResolvedLibrary(Library library) {
278276 return resolvedLibrary .get ();
279277 }
280278
279+ }
280+
281+ private static final class CheckDependencyManagementAlignment extends ResolvedLibraryCheck {
282+
283+ private final BomResolver bomResolver ;
284+
285+ private CheckDependencyManagementAlignment (Provider <ResolvedBom > resolvedBom ,
286+ ConfigurationContainer configurations , DependencyHandler dependencies ) {
287+ super (resolvedBom );
288+ this .bomResolver = new BomResolver (configurations , dependencies );
289+ }
290+
291+ @ Override
292+ public List <String > check (Library library , ResolvedLibrary resolvedLibrary ) {
293+ List <String > errors = new ArrayList <>();
294+ String alignsWithBom = library .getAlignsWithBom ();
295+ if (alignsWithBom != null ) {
296+ Bom mavenBom = this .bomResolver
297+ .resolveMavenBom (alignsWithBom + ":" + library .getVersion ().getVersion ());
298+ checkDependencyManagementAlignment (resolvedLibrary , mavenBom , errors );
299+ }
300+ return errors ;
301+ }
302+
281303 private void checkDependencyManagementAlignment (ResolvedLibrary library , Bom mavenBom , List <String > errors ) {
282304 List <Id > managedByLibrary = library .managedDependencies ();
283305 List <Id > managedByBom = managedDependenciesOf (mavenBom );
@@ -316,4 +338,108 @@ private List<Id> managedDependenciesOf(Bom mavenBom) {
316338
317339 }
318340
341+ private static final class CheckForUnwantedDependencyManagement extends ResolvedLibraryCheck {
342+
343+ private CheckForUnwantedDependencyManagement (Provider <ResolvedBom > resolvedBom ) {
344+ super (resolvedBom );
345+ }
346+
347+ @ Override
348+ public List <String > check (Library library , ResolvedLibrary resolvedLibrary ) {
349+ Map <String , Set <String >> unwanted = findUnwantedDependencyManagement (library , resolvedLibrary );
350+ List <String > errors = new ArrayList <>();
351+ if (!unwanted .isEmpty ()) {
352+ StringBuilder error = new StringBuilder ("Unwanted dependency management:" );
353+ unwanted .forEach ((bom , dependencies ) -> {
354+ error .append ("%n - %s:" .formatted (bom ));
355+ error .append ("%n - %s" .formatted (String .join ("\n - " , dependencies )));
356+ });
357+ errors .add (error .toString ());
358+ }
359+ Map <String , Set <String >> unnecessary = findUnnecessaryPermittedDependencies (library , resolvedLibrary );
360+ if (!unnecessary .isEmpty ()) {
361+ StringBuilder error = new StringBuilder ("Dependencies permitted unnecessarily:" );
362+ unnecessary .forEach ((bom , dependencies ) -> {
363+ error .append ("%n - %s:" .formatted (bom ));
364+ error .append ("%n - %s" .formatted (String .join ("\n - " , dependencies )));
365+ });
366+ errors .add (error .toString ());
367+ }
368+ return errors ;
369+ }
370+
371+ private Map <String , Set <String >> findUnwantedDependencyManagement (Library library ,
372+ ResolvedLibrary resolvedLibrary ) {
373+ Map <String , Set <String >> unwanted = new LinkedHashMap <>();
374+ for (Bom bom : resolvedLibrary .importedBoms ()) {
375+ Set <String > notPermitted = new TreeSet <>();
376+ Set <Id > managedDependencies = managedDependenciesOf (bom );
377+ managedDependencies .stream ()
378+ .filter ((dependency ) -> unwanted (bom , dependency , findPermittedDependencies (library , bom )))
379+ .map (Id ::toString )
380+ .forEach (notPermitted ::add );
381+ if (!notPermitted .isEmpty ()) {
382+ unwanted .put (bom .id ().artifactId (), notPermitted );
383+ }
384+ }
385+ return unwanted ;
386+ }
387+
388+ private List <PermittedDependency > findPermittedDependencies (Library library , Bom bom ) {
389+ for (Group group : library .getGroups ()) {
390+ for (ImportedBom importedBom : group .getBoms ()) {
391+ if (importedBom .name ().equals (bom .id ().artifactId ()) && group .getId ().equals (bom .id ().groupId ())) {
392+ return importedBom .permittedDependencies ();
393+ }
394+ }
395+ }
396+ return Collections .emptyList ();
397+ }
398+
399+ private Set <Id > managedDependenciesOf (Bom bom ) {
400+ Set <Id > managedDependencies = new TreeSet <>();
401+ if (bom != null ) {
402+ managedDependencies .addAll (bom .managedDependencies ());
403+ managedDependencies .addAll (managedDependenciesOf (bom .parent ()));
404+ for (Bom importedBom : bom .importedBoms ()) {
405+ managedDependencies .addAll (managedDependenciesOf (importedBom ));
406+ }
407+ }
408+ return managedDependencies ;
409+ }
410+
411+ private boolean unwanted (Bom bom , Id managedDependency , List <PermittedDependency > permittedDependencies ) {
412+ if (bom .id ().groupId ().equals (managedDependency .groupId ())
413+ || managedDependency .groupId ().startsWith (bom .id ().groupId () + "." )) {
414+ return false ;
415+ }
416+ for (PermittedDependency permittedDependency : permittedDependencies ) {
417+ if (permittedDependency .artifactId ().equals (managedDependency .artifactId ())
418+ && permittedDependency .groupId ().equals (managedDependency .groupId ())) {
419+ return false ;
420+ }
421+ }
422+ return true ;
423+ }
424+
425+ private Map <String , Set <String >> findUnnecessaryPermittedDependencies (Library library ,
426+ ResolvedLibrary resolvedLibrary ) {
427+ Map <String , Set <String >> unnecessary = new HashMap <>();
428+ for (Bom bom : resolvedLibrary .importedBoms ()) {
429+ Set <String > permittedDependencies = findPermittedDependencies (library , bom ).stream ()
430+ .map ((dependency ) -> dependency .groupId () + ":" + dependency .artifactId ())
431+ .collect (Collectors .toCollection (TreeSet ::new ));
432+ Set <String > dependencies = managedDependenciesOf (bom ).stream ()
433+ .map ((dependency ) -> dependency .groupId () + ":" + dependency .artifactId ())
434+ .collect (Collectors .toCollection (TreeSet ::new ));
435+ permittedDependencies .removeAll (dependencies );
436+ if (!permittedDependencies .isEmpty ()) {
437+ unnecessary .put (bom .id ().artifactId (), permittedDependencies );
438+ }
439+ }
440+ return unnecessary ;
441+ }
442+
443+ }
444+
319445}
0 commit comments