Skip to content
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

Have HandlerInterceptor being ScopedValue friendly #32837

Closed
Mumeii opened this issue May 16, 2024 · 1 comment
Closed

Have HandlerInterceptor being ScopedValue friendly #32837

Mumeii opened this issue May 16, 2024 · 1 comment
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: declined A suggestion or change that we don't feel we should currently apply

Comments

@Mumeii
Copy link

Mumeii commented May 16, 2024

Hi

Since the arrival of Java 21 and the virtual threads, I'm trying to make the most use of it while working on a Proof Of Concept

My goal is to :

  1. setup a classical Spring Boot MVC, REST API, project
  2. then use the springthreads.virtual.enabled: true flag
  3. from this point, see what I can replace in my controlers / services / etc with the use of :
    • either StructuredTaskScope
    • or ScopedValue

I've reached the third step and globally everything is going pretty smoothly.

Unfortunately, I've hit a corner case for which one, so far, I can't see any straightforward way to work around ... at least without changing Spring boot framework

This case is the following :

Setup an HandlerInterceptor that init a ScopedValue according to the received request's properties

By design I guess, the current ScopedValue API disallow accessing to Scoped Value outside of the lambas passed to its run, call or get(Supplier<? extends R> op) methods.

I.E. the beast code I've managed to produce is this one :

	@Override
	public boolean preHandle(
		final HttpServletRequest request,
		final HttpServletResponse response,
		final Object handler
	) {
		ScopedValue.where(
			SCOPED_USER_ID,
			getUserIdFromRequest( request )
		);

		// Following logging fails on a NoSuchElementException, because the previous where 
		// call has only effects on child lambdas it can trigger ... 
		log.debug( STR."Found in SCOPED_USER_ID : \{SCOPED_USER_ID.get()}" );

                return true;
	}

Which, as you can see, fails on a NoSuchElementException

Thus, the only solution I can imagine right now, with the current ScopedValue API constraints, is to find a way to have the rest of the processing chain been performed within the lambda passed to one of the previously mentionned methods :

	@Override
	public boolean preHandle(
		final HttpServletRequest request,
		final HttpServletResponse response,
		final Object handler,
		final InterceptorChain chain
	) {
		ScopedValue
			.where( SCOPED_USER_ID, getUserIdFromRequest( request ) )
			.run( () -> {

				// here it's ok :
				log.debug( STR."[INSIDE] Found SCOPED_USER_ID : \{SCOPED_USER_ID.get()}" );

				// but ... the rest of the processing chain MUST unravel within this lambda :) ...
				chain.next(); // ??
			} );

		return true;
	}

But it would necessitate to really nest the subsequent interceptor's call, and thus, to change the way Spring Boot's HandlerInterceptor works.

Maybe by introducing some kind of continuous passing style system, a bit like what is done in the Filters ?

For now on, I've decided to go for a Filter, but it don't feel right

My two other options either defeat the purpose of my POC, or the point of using a HandlerInterceptor :

  1. Use a ThreadLocal 😋
  2. Have ALL of my controller's endpoints fetching the request details (headers, etc ...), and perform themself the ScopedValue.where( ... ).run( ... )

I guess they are many other patterns that could help copping with this limitation.

Looking forward to see your advices regarding this problematic !

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label May 16, 2024
@bclozel bclozel transferred this issue from spring-projects/spring-boot May 16, 2024
@bclozel bclozel added the in: web Issues in web modules (web, webmvc, webflux, websocket) label May 16, 2024
@bclozel
Copy link
Member

bclozel commented Jun 4, 2024

Thanks for reaching out. You are right, HandlerInterceptor is meant to act at different stages of the request handling and provide decision points.

HandlerInterceptor is basically similar to a Servlet Filter, but in contrast to the latter it just allows custom pre-processing with the option of prohibiting the execution of the handler itself, and custom post-processing. Filters are more powerful, for example they allow for exchanging the request and response objects that are handed down the chain. Note that a filter gets configured in web. xml, a HandlerInterceptor in the application context.

In that sense, the Servlet Filter contract is much more appropriate for the use case you're showing.

Right now scoped values are in third preview for Java 23 and I don't think we should introduce new contracts until we have a clearer vision and concrete use cases for this.

@bclozel bclozel closed this as not planned Won't fix, can't repro, duplicate, stale Jun 4, 2024
@bclozel bclozel added status: declined A suggestion or change that we don't feel we should currently apply and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Jun 4, 2024
@bclozel bclozel self-assigned this Jun 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: declined A suggestion or change that we don't feel we should currently apply
Projects
None yet
Development

No branches or pull requests

3 participants