Skip to content

BeanPostProcessor architecture rules do not work with external classes #45202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from

Conversation

nosan
Copy link
Contributor

@nosan nosan commented Apr 15, 2025

See #45196

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Apr 15, 2025
@nosan
Copy link
Contributor Author

nosan commented Apr 15, 2025

  1. Is there a way to configure the classpath for a custom Gradle task so that these classes are accessible during its execution? (I have managed this myself by using the context ClassLoader.)
  2. Do you have any suggestions on how to write a unit test for this?

This works because com.tngtech.archunit.core.importer.resolvers.ClassResolverFromClasspath tries to resolve any missing classes by using Thread.currentThread().getContextClassLoader()

@nosan nosan changed the title Use ClassLoader with ArchitectureCheck BeanPostProcessor architecture rules do not work with external classes Apr 15, 2025
@wilkinsona
Copy link
Member

wilkinsona commented Apr 15, 2025

Thanks, @nosan. I think that the "official" Gradle way of doing this would be to use WorkerExecutor and its support for class loader isolation. That may be slightly safer than changing the thread context class loader which I guess could have other unexpected side-effects.

@nosan
Copy link
Contributor Author

nosan commented Apr 15, 2025

Thanks, @wilkinsona

I will take a look.

@nosan
Copy link
Contributor Author

nosan commented Apr 15, 2025

@wilkinsona

I've been trying to use WorkerExecutor and pass rules to the underlying WorkAction via WorkParameters.

* What went wrong:
  Execution failed for task ':spring-boot-project:spring-boot-tools:spring-boot-loader-tools:checkArchitectureMain'.
 A failure occurred while executing org.springframework.boot.build.architecture.ArchitectureWorkAction
 Could not isolate value org.springframework.boot.build.architecture.ArchitectureWorkParameters_Decorated@1a9f8595 of type ArchitectureWorkParameters
 Could not serialize value of type ArchRule.Factory.SimpleArchRule

From my understanding, rules are not serializable. Do you have any suggestions on how to handle this?

@wilkinsona
Copy link
Member

Hmm, perhaps using the worker API isn't such a good idea after all. Apologies for the dead end.

I don't recall the details of your original approach, but an alternative to manipulating the thread context class loader would be a custom ClassResolver but that's not entirely straightforward as it only supports String arguments so a FileCollection (for example), couldn't be passed straight to it.

Perhaps it's better to just stick with setting the TCCL. We can reconsider if it causes problems in the future.

@nosan nosan marked this pull request as draft April 15, 2025 15:08
@philwebb philwebb added type: task A general task and removed status: waiting-for-triage An issue we've not yet triaged labels Apr 15, 2025
@philwebb philwebb added this to the 3.3.x milestone Apr 15, 2025
@nosan
Copy link
Contributor Author

nosan commented Apr 15, 2025

@philwebb
Please don't merge it. I would like to revisit a custom ClassResolver approach once more.

@philwebb philwebb added the status: on-hold We can't start working on this issue yet label Apr 15, 2025
@nosan nosan force-pushed the arch-unit-dependency branch from 593cba2 to 6c9b61d Compare April 15, 2025 19:04
@nosan nosan marked this pull request as ready for review April 15, 2025 19:06
@nosan nosan force-pushed the arch-unit-dependency branch from 6c9b61d to c2a401b Compare April 15, 2025 19:31
@nosan
Copy link
Contributor Author

nosan commented Apr 15, 2025

I've updated this PR to utilize a custom ClassResolver instead of TCCL.

Additionally, I made updates to the ArchitectureCheckTests to use GradleRunner after noticing that all tests were passing regardless of modifications. You can confirm this by altering any test, and it will have no effect.

I also added a test to verify external BPP.

Builed failed due to:

> Task :spring-boot-project:spring-boot-dependencies:bomrCheck FAILED

Neo4j Java Driver
    - Version 5.28.3 is misaligned. It should be 5.28.4.

@nosan
Copy link
Contributor Author

nosan commented Apr 15, 2025

I can fix ArchitectureCheckTests in a separate PR if needed.

@nosan
Copy link
Contributor Author

nosan commented Apr 15, 2025

ArchitectureCheckTests can also be fixed with:

	Project project = ProjectBuilder.builder().withProjectDir(projectDir).build();
		project.getTasks().register("checkArchitecture", ArchitectureCheck.class, (task) -> {
			task.setClasses(project.files("classes"));
			callback.accept(task);
			
		}).get();   //This line triggers a callback but is currently being missed
		

But I still need GradleRunner to verify external BPP.

@nosan nosan force-pushed the arch-unit-dependency branch 7 times, most recently from 66264d2 to 09faa83 Compare April 16, 2025 13:13
nosan added 2 commits April 16, 2025 19:03
Prior to this commit, certain rules, such as `BeanPostProcessor`,
did not work with external classes. This commit ensures that
`ArchRules` are executed using the `CompileClasspathClassResolver`,
which resolves any missing classes. This helps build a Class Graph
for external classes.

Signed-off-by: Dmytro Nosan <[email protected]>
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]>
@nosan nosan force-pushed the arch-unit-dependency branch from 09faa83 to 5baa6d9 Compare April 16, 2025 16:03
@nosan
Copy link
Contributor Author

nosan commented Apr 16, 2025

@wilkinsona

don't recall the details of your original approach, but an alternative to manipulating the thread context class loader would be a custom ClassResolver but that's not entirely straightforward as it only supports String arguments so a FileCollection (for example), couldn't be passed straight to it.

Unfortunately, this approach has a significant limitation: it is not possible to pass a FileCollection directly. It can only be passed as a String value, regardless of the method used, whether through constructor parameters or Extension properties. For some reason, ArchUnit restricts the value type to strings. This means the FileCollection must be represented as a String using a separator (such as ',' or ':'). However, this introduces a potential issue: what happens if the file paths themselves contain the chosen delimiter?

Perhaps it's better to just stick with setting the TCCL. We can reconsider if it causes problems in the future.

Sticking with setting the TCCL seems better

@nosan
Copy link
Contributor Author

nosan commented Apr 16, 2025

This PR includes two commits:

  • The first implements a custom ClassResolver approach.
  • The second utilizes the Thread Context ClassLoader (TCCL).

@wilkinsona wilkinsona self-assigned this Apr 17, 2025
@wilkinsona wilkinsona removed the status: on-hold We can't start working on this issue yet label Apr 17, 2025
@wilkinsona wilkinsona modified the milestones: 3.3.x, 3.3.11 Apr 17, 2025
wilkinsona pushed a commit that referenced this pull request Apr 17, 2025
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.

See gh-45202

Signed-off-by: Dmytro Nosan <[email protected]>
wilkinsona added a commit that referenced this pull request Apr 17, 2025
@wilkinsona
Copy link
Member

Thanks very much, @nosan. I went with the TCCL-based approach in the end.

@nosan
Copy link
Contributor Author

nosan commented Apr 17, 2025

Thanks, @wilkinsona

I came across the commit a572283, and I was wondering if this @Role check should be backported to the 3.3.x/3.4.x branches?

@wilkinsona
Copy link
Member

I don't think it needs to be as we don't have any BPPs in those branches that use infrastructure beans. We can backport the slight relaxation of the rule in the future if needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: task A general task
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants