diff --git a/buildSrc/src/main/java/org/springframework/boot/build/classpath/CheckClasspathForUnnecessaryExclusions.java b/buildSrc/src/main/java/org/springframework/boot/build/classpath/CheckClasspathForUnnecessaryExclusions.java index 7270812fe41..797f2df3fbd 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/classpath/CheckClasspathForUnnecessaryExclusions.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/classpath/CheckClasspathForUnnecessaryExclusions.java @@ -38,6 +38,7 @@ import org.gradle.api.artifacts.ExcludeRule; import org.gradle.api.artifacts.ModuleDependency; import org.gradle.api.artifacts.component.ModuleComponentIdentifier; import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.artifacts.result.ResolvedArtifactResult; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.TaskAction; @@ -48,6 +49,9 @@ import org.gradle.api.tasks.TaskAction; */ public class CheckClasspathForUnnecessaryExclusions extends DefaultTask { + private static final Map SPRING_BOOT_DEPENDENCIES_PROJECT = Collections.singletonMap("path", + ":spring-boot-project:spring-boot-dependencies"); + private final Map> exclusionsByDependencyId = new TreeMap<>(); private final Map dependencyById = new HashMap<>(); @@ -63,27 +67,31 @@ public class CheckClasspathForUnnecessaryExclusions extends DefaultTask { ConfigurationContainer configurations) { this.dependencyHandler = getProject().getDependencies(); this.configurations = getProject().getConfigurations(); - this.platform = this.dependencyHandler.create(this.dependencyHandler.platform(this.dependencyHandler - .project(Collections.singletonMap("path", ":spring-boot-project:spring-boot-dependencies")))); + this.platform = this.dependencyHandler.create( + this.dependencyHandler.platform(this.dependencyHandler.project(SPRING_BOOT_DEPENDENCIES_PROJECT))); getOutputs().upToDateWhen((task) -> true); } public void setClasspath(Configuration classpath) { this.exclusionsByDependencyId.clear(); this.dependencyById.clear(); - classpath.getAllDependencies().all((dependency) -> { - if (dependency instanceof ModuleDependency) { - String dependencyId = dependency.getGroup() + ":" + dependency.getName(); - Set excludeRules = ((ModuleDependency) dependency).getExcludeRules(); - TreeSet exclusions = excludeRules.stream() - .map((rule) -> rule.getGroup() + ":" + rule.getModule()) - .collect(Collectors.toCollection(TreeSet::new)); - this.exclusionsByDependencyId.put(dependencyId, exclusions); - if (!exclusions.isEmpty()) { - this.dependencyById.put(dependencyId, getProject().getDependencies().create(dependencyId)); - } - } - }); + classpath.getAllDependencies().all(this::processDependency); + } + + private void processDependency(Dependency dependency) { + if (dependency instanceof ModuleDependency) { + processDependency((ModuleDependency) dependency); + } + } + + private void processDependency(ModuleDependency dependency) { + String dependencyId = getId(dependency); + TreeSet exclusions = dependency.getExcludeRules().stream().map(this::getId) + .collect(Collectors.toCollection(TreeSet::new)); + this.exclusionsByDependencyId.put(dependencyId, exclusions); + if (!exclusions.isEmpty()) { + this.dependencyById.put(dependencyId, getProject().getDependencies().create(dependencyId)); + } } @Input @@ -94,33 +102,55 @@ public class CheckClasspathForUnnecessaryExclusions extends DefaultTask { @TaskAction public void checkForUnnecessaryExclusions() { Map> unnecessaryExclusions = new HashMap<>(); - for (Entry> entry : this.exclusionsByDependencyId.entrySet()) { - String dependencyId = entry.getKey(); - Set exclusions = entry.getValue(); + this.exclusionsByDependencyId.forEach((dependencyId, exclusions) -> { if (!exclusions.isEmpty()) { Dependency toCheck = this.dependencyById.get(dependencyId); List dependencies = this.configurations.detachedConfiguration(toCheck, this.platform) - .getIncoming().getArtifacts().getArtifacts().stream().map((artifact) -> { - ModuleComponentIdentifier id = (ModuleComponentIdentifier) artifact.getId() - .getComponentIdentifier(); - return id.getGroup() + ":" + id.getModule(); - }).collect(Collectors.toList()); + .getIncoming().getArtifacts().getArtifacts().stream().map(this::getId) + .collect(Collectors.toList()); exclusions.removeAll(dependencies); + removeProfileExclusions(dependencyId, exclusions); if (!exclusions.isEmpty()) { unnecessaryExclusions.put(dependencyId, exclusions); } } - } + }); if (!unnecessaryExclusions.isEmpty()) { - StringBuilder message = new StringBuilder("Unnecessary exclusions detected:"); - for (Entry> entry : unnecessaryExclusions.entrySet()) { - message.append(String.format("%n %s", entry.getKey())); - for (String exclusion : entry.getValue()) { - message.append(String.format("%n %s", exclusion)); - } + throw new GradleException(getExceptionMessage(unnecessaryExclusions)); + } + } + + private void removeProfileExclusions(String dependencyId, Set exclusions) { + if ("org.xmlunit:xmlunit-core".equals(dependencyId)) { + exclusions.remove("javax.xml.bind:jaxb-api"); + } + } + + private String getExceptionMessage(Map> unnecessaryExclusions) { + StringBuilder message = new StringBuilder("Unnecessary exclusions detected:"); + for (Entry> entry : unnecessaryExclusions.entrySet()) { + message.append(String.format("%n %s", entry.getKey())); + for (String exclusion : entry.getValue()) { + message.append(String.format("%n %s", exclusion)); } - throw new GradleException(message.toString()); } + return message.toString(); + } + + private String getId(ResolvedArtifactResult artifact) { + return getId((ModuleComponentIdentifier) artifact.getId().getComponentIdentifier()); + } + + private String getId(ModuleDependency dependency) { + return dependency.getGroup() + ":" + dependency.getName(); + } + + private String getId(ExcludeRule rule) { + return rule.getGroup() + ":" + rule.getModule(); + } + + private String getId(ModuleComponentIdentifier identifier) { + return identifier.getGroup() + ":" + identifier.getModule(); } } diff --git a/spring-boot-project/spring-boot-starters/spring-boot-starter-test/build.gradle b/spring-boot-project/spring-boot-starters/spring-boot-starter-test/build.gradle index 6e5d4e409f5..74ba78aab07 100644 --- a/spring-boot-project/spring-boot-starters/spring-boot-starter-test/build.gradle +++ b/spring-boot-project/spring-boot-starters/spring-boot-starter-test/build.gradle @@ -9,6 +9,7 @@ dependencies { api(project(":spring-boot-project:spring-boot-test")) api(project(":spring-boot-project:spring-boot-test-autoconfigure")) api("com.jayway.jsonpath:json-path") + api("jakarta.xml.bind:jakarta.xml.bind-api") api("org.assertj:assertj-core") api("org.hamcrest:hamcrest") api("org.junit.jupiter:junit-jupiter") @@ -17,5 +18,7 @@ dependencies { api("org.skyscreamer:jsonassert") api("org.springframework:spring-core") api("org.springframework:spring-test") - api("org.xmlunit:xmlunit-core") + api("org.xmlunit:xmlunit-core") { + exclude group: "javax.xml.bind", module: "jaxb-api" + } }