Skip to content

fix: isolate Spring session-scoped beans per user in multi-user tests#111

Open
totally-not-ai[bot] wants to merge 3 commits into
mainfrom
fix/multi-user-session-scope-isolation
Open

fix: isolate Spring session-scoped beans per user in multi-user tests#111
totally-not-ai[bot] wants to merge 3 commits into
mainfrom
fix/multi-user-session-scope-isolation

Conversation

@totally-not-ai

Copy link
Copy Markdown

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.

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
mcollovati previously approved these changes Jun 25, 2026

@mcollovati mcollovati left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Multi-user app context does not isolate Spring session-scoped beans per user

1 participant