fix: isolate Spring session-scoped beans per user in multi-user tests#111
Open
totally-not-ai[bot] wants to merge 3 commits into
Open
fix: isolate Spring session-scoped beans per user in multi-user tests#111totally-not-ai[bot] wants to merge 3 commits into
totally-not-ai[bot] wants to merge 3 commits into
Conversation
Multi-user browserless tests (newUser() -> newWindow()) gave each user its own VaadinSession, but Spring's @SessionScope/@requestScope beans are resolved through RequestContextHolder, which is bound to a single request for the whole test thread (e.g. by Spring's ServletTestExecutionListener). As a result every user resolved session-scoped beans against the same request, so the second user reused the first user's instance. When such a bean held a local ValueSignal, binding it in the second user's UI threw the cross-session "created in one VaadinSession but accessed from another" IllegalStateException (issue #110). Add a per-user RequestContextHandler hook in the shared multi-user lifecycle, mirroring the existing SecurityContextHandler: BrowserlessUserContext saves the thread's current binding, binds the active user's request whenever its session thread-locals are applied (creation, window activation, close), and clears it on teardown. The Spring integration wires a SpringRequestContextHandler that binds each user's request to RequestContextHolder via ServletRequestAttributes (backed by that user's MockHttpSession) and registers the standard request/ session web scopes when missing, so @SessionScope/@requestScope resolve in the mock environment. The hook is null/no-op for non-Spring (plain/JUnit) contexts. @VaadinSessionScope already isolated correctly because it resolves against VaadinSession.getCurrent(); this fix brings Spring's standard web scopes in line.
mcollovati
previously approved these changes
Jun 25, 2026
mcollovati
left a comment
Contributor
There was a problem hiding this comment.
Overall looks good.
non-blockers:
- should we add a test for RequestScope as well? Does it make sense?
- we should ensure the same feature works for Quarkus as well. Maybe better do it in a separate PR
The notion of a per-request scope is ambiguous in browserless tests (there is no real request boundary), so drop the @requestScope bean/view/test and the request web-scope registration. The handler still binds each user's request to RequestContextHolder, which is what makes @SessionScope resolve per user.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Multi-user browserless tests (newUser() -> newWindow()) gave each user its
own VaadinSession, but Spring's @SessionScope/@requestScope beans are resolved
through RequestContextHolder, which is bound to a single request for the whole
test thread (e.g. by Spring's ServletTestExecutionListener). As a result every
user resolved session-scoped beans against the same request, so the second
user reused the first user's instance. When such a bean held a local
ValueSignal, binding it in the second user's UI threw the cross-session
"created in one VaadinSession but accessed from another" IllegalStateException
(issue #110).
Add a per-user RequestContextHandler hook in the shared multi-user lifecycle,
mirroring the existing SecurityContextHandler: BrowserlessUserContext saves the
thread's current binding, binds the active user's request whenever its session
thread-locals are applied (creation, window activation, close), and clears it
on teardown. The Spring integration wires a SpringRequestContextHandler that
binds each user's request to RequestContextHolder via ServletRequestAttributes
(backed by that user's MockHttpSession) and registers the standard request/
session web scopes when missing, so @SessionScope/@requestScope resolve in the
mock environment. The hook is null/no-op for non-Spring (plain/JUnit) contexts.
@VaadinSessionScope already isolated correctly because it resolves against
VaadinSession.getCurrent(); this fix brings Spring's standard web scopes in
line.