1

Is there a way to intercept SimpleAsyncTaskExecutor? Basically I am trying to intercept every time SimpleAsyncTaskExecutor is invoked.

On top of that what I am really trying to do is pass on a RequestScope bean. I found How to enable request scope in async task executor, but the problem is that I cannot reuse threads. I need a new thread created every time.

Any idea how I could forward Request Scoped beans to an async thread or how I could intercept @Async for a SimpleAsyncTaskExecutor?

Thanks, Brian

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
Brian
  • 556
  • 6
  • 26
  • 1
    Don't. There is no request an dholding on longer to request scoped objects will lead to issues. Just pass the data you need to the `@Async` method as an argument. Or use a proper `TaskDecorator` implementation which copies parts of it to a thread local (and cleans it after the execution has finished). That way you can use a pool of threads instead of creating new threads each time you need one (those will linger around and eventually blow up your system!). – M. Deinum Jun 30 '20 at 05:47
  • Would you have any links to a good example of a TaskDecorator pattern? – Brian Jun 30 '20 at 13:06
  • Did you like the second answer I provided better? – Brian Jun 30 '20 at 14:08

2 Answers2

0

I'm not sure this is the best way to accomplish what I am trying to accomplish but it seems to work.

Config:

@Configuration
@EnableAsync
public class Config extends AsyncConfigurerSupport implements WebMvcConfigurer {
    
    @Override
    @Bean
    public Executor getAsyncExecutor() {
        return new SimpleAsyncTaskExecutor(new MyThreadFactory());
    }
}

MyThreadFactory:

public class MyThreadFactory implements ThreadFactory {

    @Override
    public Thread newThread(Runnable r) {
        RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);
        return new Thread(r);
    }

}
Brian
  • 556
  • 6
  • 26
  • The downside of this is long lived systems often favor using a threadpool for performance which this approach would be incompatible with. Is there any reason you cant just change the async method to take the request scoped bean as a param? – Deadron Jun 29 '20 at 18:39
  • Why would this solution be incompatible with a long lived server? The reason this would not be possible, is that I am grabbing some keys from the request to add to my interceptors that logs some data to the server. I log the keys so I can find them in the logs and correlate which logs go with which request – Brian Jun 29 '20 at 19:42
  • Incompatible isn't quite what I meant. The problem is if you add all the request attributes to the thread but don't remove them when you are done it could cause you issues. – Deadron Jun 29 '20 at 20:21
  • Why would that cause a problem? Why would Garbage collection not clean up those attributes and threads? – Brian Jun 29 '20 at 20:28
  • SimpleAsyncTaskExecutor is never reused is my understanding...Am I misunderstanding? https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/task/SimpleAsyncTaskExecutor.html – Brian Jun 29 '20 at 20:37
  • You are correct. However, in most environments you want to use some sort of pooled executor. – Deadron Jun 29 '20 at 20:48
  • How do you define a short lived task? < 100 MS, <1 second, <1 min etc... – Brian Jun 29 '20 at 20:50
0

So I also tried another solution using ThreadPoolTaskExecutor.

Basically I used this solution: http://www.chrisport.ch/java/2016/10/12/java-spring-context-aware.html and switched MDC context stuff to final RequestAttributes originalContextCopy = RequestContextHolder.getRequestAttributes(); and RequestContextHolder.resetRequestAttributes();

Brian
  • 556
  • 6
  • 26
  • Don't. As that interferes with Spring itself (as you are now clearing the request attributes). If you don't have a request then dont' use request attributes. Copy them to somewhere else and use them. – M. Deinum Jul 01 '20 at 05:42
  • I don't know if I agree with this... I'm not ever resetting the request in the parent thread, I am only ever resetting it as the last line of processing in the async thread. Since Request Scope is thread bound it shouldn't interfere with the parent thread. The parent thread is still being managed by spring. Thus I dont understand why your saying that would interfere with spring. Can you elaborate? – Brian Jul 01 '20 at 14:26
  • Because Spring is also resetting the request variables (it isn't thread bound it is request bound, that that is thread bound by a thread from a pool is a different thing). As stated before you don't have a request in your spawned thread then don't use request things in that thread. Just copy what you need. – M. Deinum Jul 01 '20 at 14:47
  • How can I just copy what I need? there is only 1 object I really care about, but I need that object accessible from the whole thread... I cannot just pass the object around everywhere, because I need it accessible in interceptors and other things that are logging from that specific thread. – Brian Jul 01 '20 at 15:14
  • Back to the other discussion point, I am not seeing the request being reset in the parent thread... all of my data is still there in the parent threads after i reset it in the child thread. If I try to grab something from the child thread request, after I reset the request then i do get a null pointer, but I did that just for the sake of testing if it was still there in the child thread after I reset it. Point being still there in parent thread after I reset in child thread – Brian Jul 01 '20 at 15:16
  • I give up. You shouldn't be using the request-scope for a background thread that is all I'm saying, but you are still trying to shoehorn request-scope in something that doesn't have a request. An interceptor is part of the request flow and that isn't a problem, the issue is with the thread you start yourself (that doesn't belong to the request handling but is a background thread which doesn't have the slightest idea how it got started nor should you tie it to web based scopes). BUt alas if you want go ahead, but don't go asking questions when it starts to blow-up. – M. Deinum Jul 01 '20 at 17:18
  • I'm not trying to make you give up, Im truly trying to understand. the best way to do what I am trying to accomplish. Basically I have an Aspect that intercepts every proxied method call in my service, both sync methods and async methods. I intercept certain method calls with a point cut and log data. As part of that log, I log with keys. How could I get access to those keys, since I cant pass them into an pointcut? – Brian Jul 01 '20 at 18:03