Skip to content

Commit 593cba2

Browse files
committed
Use ClassLoader with ArchitectureCheck
Prior to this commit, certain rules, like BeanPostProcessor, did not work with external classes. This commit ensures that ArchRules are executed within a context ClassLoader that includes all classes from the compile classpath. Signed-off-by: Dmytro Nosan <[email protected]>
1 parent 51bf592 commit 593cba2

File tree

6 files changed

+44
-14
lines changed

6 files changed

+44
-14
lines changed

buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@
1818

1919
import java.io.File;
2020
import java.io.IOException;
21+
import java.net.URL;
22+
import java.net.URLClassLoader;
2123
import java.nio.file.Files;
2224
import java.nio.file.Path;
2325
import java.nio.file.StandardOpenOption;
26+
import java.util.ArrayList;
2427
import java.util.Collections;
2528
import java.util.List;
29+
import java.util.concurrent.Callable;
2630
import java.util.function.Supplier;
2731
import java.util.stream.Stream;
2832

@@ -33,11 +37,13 @@
3337
import org.gradle.api.DefaultTask;
3438
import org.gradle.api.Task;
3539
import org.gradle.api.Transformer;
40+
import org.gradle.api.file.ConfigurableFileCollection;
3641
import org.gradle.api.file.DirectoryProperty;
3742
import org.gradle.api.file.FileCollection;
3843
import org.gradle.api.file.FileTree;
3944
import org.gradle.api.provider.ListProperty;
4045
import org.gradle.api.provider.Property;
46+
import org.gradle.api.tasks.Classpath;
4147
import org.gradle.api.tasks.IgnoreEmptyDirectories;
4248
import org.gradle.api.tasks.Input;
4349
import org.gradle.api.tasks.InputFiles;
@@ -80,13 +86,32 @@ private List<String> asDescriptions(List<ArchRule> rules) {
8086
}
8187

8288
@TaskAction
83-
void checkArchitecture() throws IOException {
84-
JavaClasses javaClasses = new ClassFileImporter().importPaths(classFilesPaths());
85-
List<EvaluationResult> violations = evaluate(javaClasses).filter(EvaluationResult::hasViolation).toList();
86-
File outputFile = getOutputDirectory().file("failure-report.txt").get().getAsFile();
87-
writeViolationReport(violations, outputFile);
88-
if (!violations.isEmpty()) {
89-
throw new VerificationException("Architecture check failed. See '" + outputFile + "' for details.");
89+
void checkArchitecture() throws Exception {
90+
withCompileClasspath(() -> {
91+
JavaClasses javaClasses = new ClassFileImporter().importPaths(classFilesPaths());
92+
List<EvaluationResult> violations = evaluate(javaClasses).filter(EvaluationResult::hasViolation).toList();
93+
File outputFile = getOutputDirectory().file("failure-report.txt").get().getAsFile();
94+
writeViolationReport(violations, outputFile);
95+
if (!violations.isEmpty()) {
96+
throw new VerificationException("Architecture check failed. See '" + outputFile + "' for details.");
97+
}
98+
return null;
99+
});
100+
}
101+
102+
private void withCompileClasspath(Callable<?> callable) throws Exception {
103+
ClassLoader previous = Thread.currentThread().getContextClassLoader();
104+
try {
105+
List<URL> urls = new ArrayList<>();
106+
for (File file : getCompileClasspath().getFiles()) {
107+
urls.add(file.toURI().toURL());
108+
}
109+
ClassLoader classLoader = new URLClassLoader(urls.toArray(new URL[0]), getClass().getClassLoader());
110+
Thread.currentThread().setContextClassLoader(classLoader);
111+
callable.call();
112+
}
113+
finally {
114+
Thread.currentThread().setContextClassLoader(previous);
90115
}
91116
}
92117

@@ -126,6 +151,10 @@ final FileTree getInputClasses() {
126151
return this.classes.getAsFileTree();
127152
}
128153

154+
@InputFiles
155+
@Classpath
156+
public abstract ConfigurableFileCollection getCompileClasspath();
157+
129158
@Optional
130159
@InputFiles
131160
@PathSensitive(PathSensitivity.RELATIVE)

buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitecturePlugin.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,6 +49,7 @@ private void registerTasks(Project project) {
4949
TaskProvider<ArchitectureCheck> checkPackageTangles = project.getTasks()
5050
.register("checkArchitecture" + StringUtils.capitalize(sourceSet.getName()), ArchitectureCheck.class,
5151
(task) -> {
52+
task.getCompileClasspath().from(sourceSet.getCompileClasspath());
5253
task.setClasses(sourceSet.getOutput().getClassesDirs());
5354
task.getResourcesDirectory().set(sourceSet.getOutput().getResourcesDir());
5455
task.dependsOn(sourceSet.getProcessResourcesTaskName());

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/FilterOrderingIntegrationTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ MockServletWebServerFactory webServerFactory() {
103103
}
104104

105105
@Bean
106-
WebServerFactoryCustomizerBeanPostProcessor ServletWebServerCustomizerBeanPostProcessor() {
106+
static WebServerFactoryCustomizerBeanPostProcessor servletWebServerCustomizerBeanPostProcessor() {
107107
return new WebServerFactoryCustomizerBeanPostProcessor();
108108
}
109109

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/HttpEncodingAutoConfigurationTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,12 @@ OrderedFormContentFilter formContentFilter() {
211211
static class MinimalWebAutoConfiguration {
212212

213213
@Bean
214-
MockServletWebServerFactory MockServletWebServerFactory() {
214+
MockServletWebServerFactory mockServletWebServerFactory() {
215215
return new MockServletWebServerFactory();
216216
}
217217

218218
@Bean
219-
WebServerFactoryCustomizerBeanPostProcessor ServletWebServerCustomizerBeanPostProcessor() {
219+
static WebServerFactoryCustomizerBeanPostProcessor servletWebServerCustomizerBeanPostProcessor() {
220220
return new WebServerFactoryCustomizerBeanPostProcessor();
221221
}
222222

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1098,7 +1098,7 @@ ServletWebServerFactory webServerFactory() {
10981098
}
10991099

11001100
@Bean
1101-
WebServerFactoryCustomizerBeanPostProcessor ServletWebServerCustomizerBeanPostProcessor() {
1101+
static WebServerFactoryCustomizerBeanPostProcessor servletWebServerCustomizerBeanPostProcessor() {
11021102
return new WebServerFactoryCustomizerBeanPostProcessor();
11031103
}
11041104

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfigurationTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -183,7 +183,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
183183
}
184184

185185
@Bean
186-
WebServerFactoryCustomizerBeanPostProcessor ServletWebServerCustomizerBeanPostProcessor() {
186+
static WebServerFactoryCustomizerBeanPostProcessor servletWebServerCustomizerBeanPostProcessor() {
187187
return new WebServerFactoryCustomizerBeanPostProcessor();
188188
}
189189

0 commit comments

Comments
 (0)