Skip to content

@DirtiesContext, @Async and @EnableAsync(mode = AdviceMode.ASPECTJ) #30229

@kicktipp

Description

@kicktipp

Affects: <6.0.6>


This is my AsyncConfig with Spring Boot. We use Compile Time Weaving with spring-aspects

@Configuration @EnableAsync(mode = AdviceMode.ASPECTJ) public class AsyncConfig implements AsyncConfigurer { @Bean public ThreadPoolTaskExecutor threadPoolTaskExecutor() { ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); threadPoolTaskExecutor.setThreadNamePrefix("AsyncExecutor-"); threadPoolTaskExecutor.setCorePoolSize(15); threadPoolTaskExecutor.setAwaitTerminationSeconds(10); threadPoolTaskExecutor.initialize(); return threadPoolTaskExecutor; } @Bean public Executor taskExecutor() { var delegatingSecurityContextAsyncTaskExecutor = new DelegatingSecurityContextAsyncTaskExecutor(threadPoolTaskExecutor()); return delegatingSecurityContextAsyncTaskExecutor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new AsyncExceptionHandler(); } }

When I have a test like this:

 @Test @DirtiesContext public void test1() { myservice.doSomethingWithAsync() } @Test @DirtiesContext public void test2() { myservice.doSomethingWithAsync() }

I get an error in the second test:

2023-03-29T11:50:09.788+02:00 ERROR 72798 --- [o-auto-6-exec-1] de.kicktipp.core.mailutils.MailHelper : Executor [java.util.concurrent.ThreadPoolExecutor@1294ca02[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 13]] did not accept task: org.springframework.scheduling.aspectj.AbstractAsyncExecutionAspect$AbstractAsyncExecutionAspect$1@2283d58f

Debugging it I found that AsyncExecutionAspectSupport holds a reference to executors in line 76:

private final Map<Method, AsyncTaskExecutor> executors = new ConcurrentHashMap<>(16); 

This AsyncExecutionAspectSupport is not reloaded when the context is reloaded. It holds a reference to the old executor from the first context. This executor is of course terminated and should not be used.

Workaround:

import org.springframework.beans.factory.DisposableBean; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect; import org.springframework.stereotype.Component; import org.springframework.util.ReflectionUtils; import java.lang.reflect.Method; import java.util.Map; @Component public class DirtiesContextWithAsyncAspectJFix implements DisposableBean { @Override public void destroy() { try { var asyncSupport = AnnotationAsyncExecutionAspect.aspectOf(); var field = ReflectionUtils.findField(asyncSupport.getClass(), "executors"); assert field != null; field.setAccessible(true); @SuppressWarnings("unchecked") var executors = (Map<Method, AsyncTaskExecutor>) field.get(asyncSupport); executors.clear(); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } }

AsyncExecutionAspectSupport should reset its executors cache on context shutdown.

Metadata

Metadata

Assignees

No one assigned

    Labels

    in: testIssues in the test modulestatus: duplicateA duplicate of another issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions