3

I try to use @Async de spring, And in my service I use a bean with scope session, always I got the bellow error:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.classSession': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No session found and request already completed - cannot create new session!

As is mentioned, No session found and request already completed. I implemented AsyncConfigurer in order to overide the ThreadPoolTaskExecutor: bellow my code

Controller:

@Autowired 
MyService myService;

@RequestMapping(value = "/doIt", method = RequestMethod.PUT)
public HttpEntity initiateCurrent(..){

myService.do();
...
}

MyService

@Autowired 
ClassWithScopeSession classSession;

@Async("taskExecutor")
public void do(){
....
...
classSession.doService();
}

//Overide the ThreadPoolTaskExecutor

public class ContextAwareCallable<T> implements Callable<T> {
    private Callable<T> task;
    private RequestAttributes context;

    @Autowired
    private ApplicationContext appContext;

    public ContextAwareCallable(Callable<T> task, RequestAttributes context) {
        this.task = task;
        this.context = context;
    }

    @Override
    public T call() throws Exception {
        if (context != null) {
            RequestContextHolder.setRequestAttributes(context);
        }
        try {
            return task.call();
        } finally {
            RequestContextHolder.resetRequestAttributes();
        }
    }
}

public class ContextAwarePoolExecutor extends ThreadPoolTaskExecutor {
    @Override
    public <T> Future<T> submit(Callable<T> task) {
        return super.submit(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes()));
    }

    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
        return super.submitListenable(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes()));
    }
}

@Configuration
@EnableAsync
public class ExecutorConfig  implements AsyncConfigurer {

    @Override
    @Bean(name="taskExecutor")
    public Executor getAsyncExecutor() {
        return new ContextAwarePoolExecutor();
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return null;
    }
}

I followed this response

BERGUIGA Mohamed Amine
  • 6,094
  • 3
  • 40
  • 38

2 Answers2

3

You need to override execute method instead:

@Override
public void execute(Runnable task) {
    super.execute(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes()));
}

And update your ContextAwareCallable to:

public class ContextAwareCallable implements Runnable {

    private Runnable task;
    private RequestAttributes context;

    public ContextAwareCallable(Runnable task, RequestAttributes context) {
        this.task = task;
        this.context = context;
    }

    @Override
    public void run() {
        if (context != null) {
            RequestContextHolder.setRequestAttributes(context);
        }

        task.run();
    }
}

I am not a guru, but this solution worked for me.

Once you annotate a method with @Async, it runs execute() method on ThreadPoolTaskExecutor class, not submit() or submitListenable().

On my solution it returns CompletableFuture. It's strange, because execute does not return anything.

Hope it will help someone.

Coderino Javarino
  • 2,819
  • 4
  • 21
  • 43
Laurynas
  • 972
  • 2
  • 11
  • 24
0

I have a multiTenant app with async methods. Someone returns a CompletableFuture. As said before, now it works for me!

@Configuration

@EnableAsync public class DashBoardAsyncExecutorConfig {

@Bean("MyTaskExecutor")
public TaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ContextAwarePoolExecutor();
    executor.setCorePoolSize(5);
    executor.setMaxPoolSize(5);
    executor.setQueueCapacity(500);
    executor.setAllowCoreThreadTimeOut(true);
    executor.setThreadNamePrefix("thread");
    executor.initialize();
    return executor;
}

}

public class ContextAwarePoolExecutor extends ThreadPoolTaskExecutor {

   @Override
   public <T> Future<T> submit(Callable<T> task) {
      return super.submit(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes()));
   }

   @Override
   public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
     return super.submitListenable(new ContextAwareCallable(task, 
     RequestContextHolder.currentRequestAttributes()));

   }
   
   //FOR COMPLETABLE FUTURE
   @Override
   public void execute(Runnable task) {
       super.execute(new ContextAwareCallableRunnable(task, RequestContextHolder.currentRequestAttributes()));
   }       

}

public class ContextAwareCallableRunnable<T> implements Runnable {
private Runnable task;
private RequestAttributes context;

public ContextAwareCallableRunnable(Runnable task, RequestAttributes context) {
    this.task = task;
    this.context = context;
}

@Override
public void run() {
    if (context != null) {
        RequestContextHolder.setRequestAttributes(context);
    }
    task.run();
}

}