Closed as not planned
Description
Describe the bug
Trying to inherit the RequestContext
to make use of the RequestAttributes
after going into an @Async
thread causes IllegalStateException
with message Cannot ask for request attribute - request is not active anymore!
.
To Reproduce
Create the following Configuration
to setup the RequestContext
inheritance (you can focus on the TaskDecorator
part only if the rest is boilerplate to you):
@EnableAsync
@Configuration(proxyBeanMethods = false)
public class AsyncTaskConfiguration {
// This is the bean that matters
@Bean
public TaskDecorator asyncTaskDecorator() {
return runnable -> {
final Optional<RequestAttributes> requestAttributes =
Optional.ofNullable(RequestContextHolder.getRequestAttributes());
return () -> {
try {
requestAttributes.ifPresent(it -> RequestContextHolder.setRequestAttributes(it, true));
runnable.run();
} finally {
requestAttributes.ifPresent(it -> RequestContextHolder.resetRequestAttributes());
}
};
};
}
// Making sure all TaskProcessors are applied without fanfare
@Bean
public BeanPostProcessor asyncBeanPostProcessor() {
return new BeanPostProcessor() {
private final List<TaskDecorator> taskDecorators = new ArrayList<>();
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof TaskDecorator decorator) {
taskDecorators.add(decorator);
}
if (bean instanceof ThreadPoolTaskExecutor taskExecutor) {
taskExecutor.setTaskDecorator(new CompositeTaskDecorator(taskDecorators));
return taskExecutor;
}
return bean;
}
};
}
// Make sure TaskProcessor beans are loaded before ThreadPoolTaskExecutor ones
@Bean
public static AbstractDependsOnBeanFactoryPostProcessor asyncTaskExecutorDependencies() {
return new AbstractDependsOnBeanFactoryPostProcessor(
ThreadPoolTaskExecutor.class,
TaskDecorator.class) {
};
}
}
The code below will then fail if the request
has already completed and the Async task is still running:
RequestContextHolder.getRequestAttributes().getAttribute("foo", SCOPE_REQUEST)
These calls still seem to work, though:
RequestContextHolder.getRequestAttributes().getAttribute("foo", SCOPE_SESSION);
((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getAttribute("foo");
The validation for this is happening in a class that hasn't changed in a long time, so maybe this is dead code now?
public Object getAttribute(String name, int scope) {
if (scope == SCOPE_REQUEST) {
if (!isRequestActive()) {
throw new IllegalStateException(
"Cannot ask for request attribute - request is not active anymore!");
}
return this.request.getAttribute(name);
}
Here is an example of a class that starts failing due to this nuance:
private static URI getCachedBaseUri() {
return (URI) getRequestAttributes().getAttribute(CACHE_KEY, RequestAttributes.SCOPE_REQUEST);
}