How to avoid ExecutorService from overridding Security Principal of a Runnable - java

When the first runnable is submitted is an inject ExecutorService, the security Principal is correctly set for that runnable. Each subsequently submitted runnable is given the security principal of the original user instead of keeping the current runnable. My development machine is running Wildfly 8.2 .
I am creating a reporting system for asynchronous processing. I created a service that checks which user created the task and ensures that only that user can start or complete the task. The code for the service is below.
#Stateless
public class ReportingService {
//EE injection security context
#Resource
SessionContext context;
//CDI security Principal
#Inject
Principal principal;
//this method handles getting the username for EE injection or CDI
private String getCurrentUser() {
if (context != null) {
return context.getCallerPrincipal().getName();
}
if (principal != null) {
return principal.getName();
}
return null;
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
#Transactional
public void registerTask(String taskId) {
//Create task
//set task.submittedBy = getCurrentUser()
//persist task
//code has been omitted since it is working
}
private void validateCurrentUserRegisteredJob(String taskId) {
String user = //get user that created task with id = id from DB
String currentUser = getCurrentUser();
if (!user.equals(currentUser)) {
throw new EJBAccesException("Current user "+currentUser+" did not register task");
}
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
#Transactional
public void startTask(String taskId) {
validateCurrentUserRegisteredJob(taskid);
//retrieve task entity, set start time to now, and merge
}
...
}
Below is my Runnable code
public TaskRunner() implements Runnable {
//CDI principal
#Inject
Principal principal;
#Inject
ReportingService rs;
private taskId;
public void setTaskId() {...}
public void run() {
log.debug("Inside Runner Current User: "+principal.getName());
rs.startTask(taskId);
....
}
}
The following is the code from the Stateless Bean that is called by a REST endpoint that kicks off the process
#Stateless
public ProjectService() {
#Inject
Instance<TaskRunner> taskRunner;
#Inject
ReportingService reportingService;
//ExecutorService that is create from Adam Bien's porcupine project
#Inject
#Dedicated
ExecutorService es;
//method that is called by rest enpoint to kick off
public void performAsynchAction(List<String> taskIds, ...rest of args...) {
taskIds.stream().forEach(t -> {
//registers task with user that made REST call
reportingService.registerTask(t);
TaskRunner runner = taskRunner.get();
runner.setTaskId(t);
log.debug("Created runner. Principal: "+runner.principal.getName());
es.submit(runner);
});
}
}
Here is the chart of the call flow
REST -> ProjectService.performAsynchAction(...)
-> reportingService.registerTask(...)
-> create CDI injected Runnable
-> submit runner to executor service
-> ExecutorService calls Runner.run()
-> rs.startTask(taskId)
I call the Rest end point as user1 for the first time and register tasks: 1-2. They all work as expected and I get the following output in my log.
Created runner. Principal: user1
Created runner. Principal: user1
Inside Runner Current User: user1
Inside Runner Current User: user1
The next time I make the same REST call as user2 and I get the following output in the log
Created runner. Principal: user2
Inside Runner Current User: user1
EJBAccessException Current user user1 did not register task
It appears that the Security Principal of the Runnable is correctly set the first time a Runnable is submitted to the ExecutorService. But for each subsequent Runneable that is submitted to the ExecutorService uses the security Principal of the first submitted runnable. Is this a bug or the intended behavior? Does anyone know of a potential work around?
EDIT: I figure out that the porcupine project I was using to create the ExecutorService was not being managed by the container. Once I switched to a ManagedExecutorService, the SessionContext was being properly propagated.
#Resource(lookup = "java:jboss/ee/concurrency/executor/customExecutor")
private ManagedExecutorService es;

I figured out the issue. I looked into the porcupine code and found out that the ExecutorService was not being managed by the Container. I created a ManagerExecutorService and the SessionContext was then being properly propogated.
#Resource(lookup = "java:jboss/ee/concurrency/executor/customExecutor")
private ManagedExecutorService es_;

I think the problem is, that you #Inject a #Dependent scoped ExecutorService into a #Stateless bean.
#Stateless beans can be pooled and reused, while #Dependent CDI beans are stored by reference and therefore are not recreated when the #Stateless bean is reused.
Without knowing the implementation of your ExecutorService, I guess that it creates contextual threads during the first run and reused them in the second run without adjusting the context.
You could "force" your ProjectService to create a new ExecutorService by encapsulating it into a #RequestScoped bean:
#RequestScoped
public class ESHolder {
#Inject
#Dedicated
ExecutorService eS;
public ExecutorService getES() {
return eS;
}
}
#Stateless
public ProjectService() {
// ...
#Inject
ESHolder esHolder;
public void performAsynchAction(List<String> taskIds, ...rest of args...) {
taskIds.stream().forEach(t -> {
// ...
esHolder.getES().submit(runner);
});
}
}

Related

How to model async service task in camunda and implement it in spring boot

I use Camunda as bpmn engine in my spring boot application
Main idea:
The first process is started in the controller, and after the response is returned to the client, the Second process should start.
I do this using #Async(spring framework) to start the second process and I have two bpmn diagrams:
firstProcess
secondProcess
Simple implementation of the idea:
#RestController
public class SimpleController {
#Autowired
private CustomService asyncService;
#Autowired
private CustomService syncService;
#GetMapping(value = "/request")
public ResponseEntity<String> sendQuestion() {
//start process described in first.bpmn
syncService.startProcess("firstProcess");
//start process described in second.bpmn asynchronously
//controller responses to client without waiting for ending secondProcess
asyncService.startProcess("secondProcess");
return new ResponseEntity<>("OK", HttpStatus.OK);
}
}
#Service
public class AsyncService implements CustomService {
#Autowired
private RuntimeService runtimeService;
#Async
public void startProcess(String key) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
//
}
runtimeService.startProcessInstanceByKey(key);
}
}
Questions:
Is there a way to do these two processes in one process (as shown at both processes)?
How should I implement this in the spring boot app?
bothProcess
You need use Call Activity Task specifying BPMN as CallActivity Type and corresponding process ids in Called Element field on the properties panel.
Also don't forget uncheck Startable checkbox for your subprocesses.

Background thread throwing HibernateException - "No Hibernate Session bound to thread..."

I need to create a process that will query a webservice to extract information, and then save the data in my database. However, because this process is very time-intensive, I would like to make it run in the background.
Currently, I have a ProcessHandler which is invoked by a button in the UI. This handler creates a Thread which should run the process in the background. However, I am getting HibernateException with the message No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here.
I have defined ProcessHandler in one of the config xml files (there are several) as follows (this is a very generic definition):
<bean class="com.project.ProcessHandler" parent="parentHandler" />
Inside ProcessHandler, the code to invoke this process is also very generic:
Thread t = new Thread(new WorkerThread(alphaManager, bravoManager, charlieManager));
t.start();
This is the current implementation of WorkerThread:
public class WorkerThread implements Runnable {
private Manager alphaManager;
private Manager bravoManager;
private Manager charlieManager;
public WorkerThread() {
this.alphaManager = null;
this.bravoManager = null;
this.charlieManager= null;
}
public WorkerThread(Manager alphaManager, Manager bravoManager, Manager charlieManager) {
this.alphaManager = alphaManager;
this.bravoManager = bravoManager;
this.charlieManager= charlieManager;
}
#Override
public void run() {
// code to query webservice and extract data...
saveToDbMethod(data);
}
#Transactional(propagation = Propagation.REQUIRED)
private void saveToDbMethod(String data) {
// code to process data...
alphaManager.save(entityA);
bravoManager.save(entityB);
charlieManager.save(entityC);
}
}
The default constructor is a leftover from when I tried to define WorkerThread as a bean in (one of) my config xml files.
Can anyone help me by giving me some tips on how to troubleshoot this?
The problem is that you create the Thread manually and expecting it behave like a spring managed bean.
As the ProcessHandler is a legitimate bean, what i would do is following:
1) Create a seaparate service class which would have the managers as dependencies and that #Transactional method:
#Service
public class Service{
private Manager alphaManager;
private Manager bravoManager;
private Manager charlieManager;
public Service(Manager alphaManager, Manager bravoManager, Manager charlieManager) {
this.alphaManager = alphaManager;
this.bravoManager = bravoManager;
this.charlieManager= charlieManager;
}
#Transactional(propagation = Propagation.REQUIRED)
private void saveToDbMethod(String data) {
// code to process data...
alphaManager.save(entityA);
bravoManager.save(entityB);
charlieManager.save(entityC);
}
}
2) Inject the Service into the ProcessHandler:
<bean class="com.project.ProcessHandler" parent="parentHandler">
<property name="service" ref="service">
</bean>
3) Finally pass the Service to the WorkerThread:
public class WorkerThread implements Runnable {
private Service service;
public WorkerThread(Service service) {
this.service = service;
}
#Override
public void run() {
// code to query webservice and extract data...
service.saveToDbMethod(data);
}
}
and:
Thread t = new Thread(new WorkerThread(service));
t.start();
Now your operations should be transactional and within a session.

Java Future - Spring Authentication is null into AuditorAware

This is my scenario:
My app has Mongo Auditing enabled, with a custom AuditorAware which gets the current user from the SecurityContext. This works well with synchronous methods, and the current auditor is successfully saved, but I can't make it work properly with #Async methods.
I have an async method (CompletableFuture) that makes some updates on my Mongo Database. When the AuditorAware.getCurrentAuditor() is called, no authentication info exists, and I can't get the current auditor (SecurityContextHolder.getContext().getAuthentication() returns null).
#Override
public User getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()
|| authentication instanceof AnonymousAuthenticationToken) {
log.error("Not authenticated");
return null;
}
[...]
}
I'm using a DelegatingSecurityContextAsyncTaskExecutor:
#Configuration
#EnableAsync
public class AsyncConfig implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(200);
executor.initialize();
return new DelegatingSecurityContextAsyncTaskExecutor(executor);
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new ItacaExceptionHandler();
}
}
How can I make it work properly?
Spring security context is always bound to Threadlocal.
Probabably you may to additionally set MODE_INHERITABLETHREADLOCAL for the security context.
#Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class);
methodInvokingFactoryBean.setTargetMethod("setStrategyName");
methodInvokingFactoryBean.setArguments(new String[]{SecurityContextHolder.MODE_INHERITABLETHREADLOCAL});
return methodInvokingFactoryBean;
}
http://www.ogrigas.eu/spring/2010/04/inherit-spring-security-context-in-child-threads
How to set up Spring Security SecurityContextHolder strategy?
Following the comments on kuhajeyan's answer, it appears you are not properly using CompletableFuture with Spring #Async.
If you launch your tasks by using e.g. CompletableFuture.supplyAsync(Supplier), they will be executed by the common ForkJoinPool and not the one you have configured for #Async. You could use the overloads that take an Executor as argument, but it would not actually benefit from the advantages of #Async.
What you should do, instead, is let Spring handle the task execution, and simply return a completed CompletableFuture like this:
#Async
public CompletableFuture<String> someMethod() {
// do some computation, but return a completed future
return CompletableFuture.completedFuture("Hello World!");
}
Spring will then execute your method asynchronously in the configured executor while immediately return a CompletableFuture which will be completed when your method returns.
If you are using Spring 4.2 or above, this is supported out of the box. Otherwise there is a bit of implementation required, but that would be for another question.

Architecture for a RESTful web service with a single thread executor?

I would like to know what could be the best architecture for a RESTful web service with a single thread executor.
My goal :
Call a RESTful web service
The web service add a task in a thread queue and execute all the task 1 per 1.
The life cyle of instanciated object is really important (there must be only one thread queue). I know that a RESTful web service life cycle is "per request" (similar to #RequestScoped I think), so I see 2 options :
Option 1 :
public class RestService {
protected final static Executor executor;
protected final static Implementation1 impl1;
protected final static Implementation2 impl2;
static {
executor = Executors.newSingleThreadExecutor();
impl1 = new Implementation1();
impl2 = new Implementation2();
}
}
#Path("/servicename")
public class MyService extends RestService {
#POST
#Path("/compute")
public void compute(){
executor.execute(new Runnable(){
public void run(){
impl1.compute();
}
});
}
}
Option 2 :
#Singleton
public class RestService {
private Executor executor;
private Implementation1 impl1;
private Implementation2 impl2;
public RestService () {
executor = Executors.newSingleThreadExecutor();
impl1 = new Implementation1();
impl2 = new Implementation2();
}
public void execute(Runnable run){
executor.execute(run);
}
public Implementation1 getImplementation1(){
return impl1;
}
public Implementation2 getImplementation2(){
return impl2;
}
}
#Path("/servicename")
public class MyService {
#Inject
private RestService rs;
#POST
#Path("/compute")
public void compute(){
rs.execute(new Runnable(){
public void run(){
rs.getImplementation1().compute();
}
});
}
}
For option 1 I'm not sure about the "life cycle" about a static field. Which option should I use ? How would you do that ?
Thanks
EDIT :
Option 3 (thread handled by EJB Container) and "ordering" is not important :
#Singleton
public class RestService {
private final Executor executor;
private final Implementation1 impl1;
private final Implementation2 impl2;
public RestService () {
executor = Executors.newSingleThreadExecutor();
impl1 = new Implementation1();
impl2 = new Implementation2();
}
public void compute1(){
executor.execute(new Runnable(){
public void run(){
impl1.compute();
}
});
}
public void compute2(){
executor.execute(new Runnable(){
public void run(){
impl2.compute();
}
});
}
}
#Path("/servicename")
public class MyService {
#Inject
private RestService rs;
#POST
#Path("/compute1")
public void compute1(){
rs.compute1();
}
#POST
#Path("/compute2")
public void compute2(){
rs.compute2();
}
}
I think Option 3 is still better than Option 1 & 2.
I think it's a bad idea to do this. Threading ought to be handled by your container, not your code.
If you're deploying on a Java EE app server, you should let it handle the threading.
If you're deploying on a non-blocking I/O server like Netty or vert.x, you should let it handle the threading.
Under no circumstances should you be managing threads.
Static fields are instantiated when the .class loads. They don't have a "lifecycle" the way instances do. They won't be cleaned up until the class loader removes the .class file.
If you must have this kind of behavior, I'd either use a JMS queue to order the processing or a producer/consumer deque to manage it. You want the processing to be asynch. Let the REST service return a token or receipt to the client and have them come back to see when the processing is done. If the line is long, you'll have no way to know when their shirt is ready. The receipt lets them come back and check when their result is available.
If you used Spring, and then separated your business logic out into separate components (beans) and injected these beans into your service class you could effectively control the threading model of your implementation simply by changing the 'scope' attribute of your business logic beans.
Here's a description of Spring bean scopes.
This approach would allow you to easily modify/experiment with your solution and discover the best alternative for your situation.
As far as architecture is concerned I would recommend that you layer your system(loose coupling, high cohesion, separation of concerns, etc.). In this regard, you would probably have a service layer (REST), a business layer, and a data (dao) layer minimum. The layers interact with each other via interfaces. Use Spring to wire things together (inject dependencies). This gives you the flexibility to inject different implementations. Example would be to inject mock DAOs for your business classes for unit tests. The service layer where your REST services live would perform the translation of requests/responses, some validation, determine which business components to invoke, and invoke them.

How to enable request scope in async task executor

In my app I have some async web services. Server accept request, return OK response and start processing request with AsyncTaskExecutor. My question is how to enable request scope here because in this processing I need to get class which is annotated by:
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
Now I get exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.requestContextImpl': Scope 'request' 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 thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
because it runs in SimpleAsyncTaskExecutor and not in DispatcherServlet
my async processing of request
taskExecutor.execute(new Runnable() {
#Override
public void run() {
asyncRequest(request);
}
});
where taskExecutor is:
<bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
We ran into the same problem - needed to execute code in the background using #Async, so it was unable to use any Session- or RequestScope beans. We solved it the following way:
Create a custom TaskPoolExecutor that stores scoped information with the tasks
Create a special Callable (or Runnable) that uses the information to set and clear the context for the background thread
Create an override configuration to use the custom executor
Note: this will only work for Session and Request scoped beans, and not for security context (as in Spring Security). You'd have to use another method to set the security context if that is what you're after.
Note2: For brevity, only shown the Callable and submit() implementation. You can do the same for the Runnable and execute().
Here is the code:
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()));
}
}
Callable:
public class ContextAwareCallable<T> implements Callable<T> {
private Callable<T> task;
private RequestAttributes context;
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();
}
}
}
Configuration:
#Configuration
public class ExecutorConfig extends AsyncConfigurerSupport {
#Override
#Bean
public Executor getAsyncExecutor() {
return new ContextAwarePoolExecutor();
}
}
The easiest way is to use a task decorator like this:
static class ContextCopyingDecorator implements TaskDecorator {
#Nonnull
#Override
public Runnable decorate(#Nonnull Runnable runnable) {
RequestAttributes context =
RequestContextHolder.currentRequestAttributes();
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
try {
RequestContextHolder.setRequestAttributes(context);
MDC.setContextMap(contextMap);
runnable.run();
} finally {
MDC.clear();
RequestContextHolder.resetRequestAttributes();
}
};
}
}
To add this decorator to the task executor, all you need is to add it in the configuration routine:
#Override
#Bean
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor poolExecutor = new ThreadPoolTaskExecutor();
poolExecutor.setTaskDecorator(new ContextCopyingDecorator());
poolExecutor.initialize();
return poolExecutor;
}
There is no need for an additional holder or a custom thread-pool task executor.
A small update for 2021: Using current versions of Spring Boot, the mere existence of a bean of type TaskDecorator will suffice. Upon creating the context, the task decorator will be used to decorate the executors that Spring Boot creates.
The solutions mentioned before were not working for me.
The reason why the solution not working is, as mentioned in #Thilak's post, as soon as the original parent thread committed response to the client, the request objects may be garbage collected.
But with some tweak to the solution provided by #Armadillo I was able to get it working. I am using spring boot 2.2
Here is what I followed.
Create a custom TaskPoolExecutor that stores(after cloning) scoped
information with the tasks.
Create a special Callable (or Runnable)
that uses the cloned information to set the current context values
and clear the context for the async thread.
Executor (Same as in #Armadillo's post):
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()));
}
}
Callable:
public class ContextAwareCallable<T> implements Callable<T> {
private Callable<T> task;
private final RequestAttributes requestAttributes;
public ContextAwareCallable(Callable<T> task, RequestAttributes requestAttributes) {
this.task = task;
this.requestAttributes = cloneRequestAttributes(requestAttributes);
}
#Override
public T call() throws Exception {
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
return task.call();
} finally {
RequestContextHolder.resetRequestAttributes();
}
}
private RequestAttributes cloneRequestAttributes(RequestAttributes requestAttributes){
RequestAttributes clonedRequestAttribute = null;
try{
clonedRequestAttribute = new ServletRequestAttributes(((ServletRequestAttributes) requestAttributes).getRequest(), ((ServletRequestAttributes) requestAttributes).getResponse());
if(requestAttributes.getAttributeNames(RequestAttributes.SCOPE_REQUEST).length>0){
for(String name: requestAttributes.getAttributeNames(RequestAttributes.SCOPE_REQUEST)){
clonedRequestAttribute.setAttribute(name,requestAttributes.getAttribute(name,RequestAttributes.SCOPE_REQUEST),RequestAttributes.SCOPE_REQUEST);
}
}
if(requestAttributes.getAttributeNames(RequestAttributes.SCOPE_SESSION).length>0){
for(String name: requestAttributes.getAttributeNames(RequestAttributes.SCOPE_SESSION)){
clonedRequestAttribute.setAttribute(name,requestAttributes.getAttribute(name,RequestAttributes.SCOPE_SESSION),RequestAttributes.SCOPE_SESSION);
}
}
if(requestAttributes.getAttributeNames(RequestAttributes.SCOPE_GLOBAL_SESSION).length>0){
for(String name: requestAttributes.getAttributeNames(RequestAttributes.SCOPE_GLOBAL_SESSION)){
clonedRequestAttribute.setAttribute(name,requestAttributes.getAttribute(name,RequestAttributes.SCOPE_GLOBAL_SESSION),RequestAttributes.SCOPE_GLOBAL_SESSION);
}
}
return clonedRequestAttribute;
}catch(Exception e){
return requestAttributes;
}
}
}
The change I made is to introduce cloneRequestAttributes() to copy and set the RequestAttribute, so that the values remain available even after the original parent thread commits response to the client.
Configuration:
Since there are other async configuration and I didn't want the behavior to be applicable in other async executors I've created its own task executor configuration.
#Configuration
#EnableAsync
public class TaskExecutorConfig {
#Bean(name = "contextAwareTaskExecutor")
public TaskExecutor getContextAwareTaskExecutor() {
ContextAwarePoolExecutor taskExecutor = new ConAwarePoolExecutor();
taskExecutor.setMaxPoolSize(20);
taskExecutor.setCorePoolSize(5);
taskExecutor.setQueueCapacity(100);
taskExecutor.setThreadNamePrefix("ContextAwareExecutor-");
return taskExecutor;
}
}
And finally on the async method, I use the executor name.
#Async("contextAwareTaskExecutor")
public void asyncMethod() {
}
Alternate Solution:
We ended up in this trouble by trying to reuse an existing component class. Though the solution made it look like it is convenient. Its much less hassle (cloning objects and reserving thread pool) if we could have referred the relevant request scoped values as method parameters. In our case, we are planning to refactor the code in such a way that the component class which is using the request scoped bean, and being reused from the async method, to accept the values as method parameters. Request scoped bean is removed from the reusable component and moved to the component class which invokes its method.
To put what I just described it in code:
Our current state is :
#Async("contextAwareTaskExecutor")
public void asyncMethod() {
reUsableCompoment.executeLogic() //This component uses the request scoped bean.
}
Refactored code:
#Async("taskExecutor")
public void asyncMethod(Object requestObject) {
reUsableCompoment.executeLogic(requestObject); //Request scoped bean is removed from the component and moved to the component class which invokes it menthod.
}
There is no way to get a request scoped object in an child async thread, since the original parent request processing thread may have already committed the response to the client and all the request objects are destroyed. One way to handle such scenarios is to use custom scope, like SimpleThreadScope.
one problem with SimpleThreadScope is that the child threads will not inherit parents scope variables, because it uses simple ThreadLocal internally. To overcome that implement a custom scope which is exactly similar to SimpleThreadScope but uses InheritableThreadLocal internally. For more info reg this
Spring MVC: How to use a request-scoped bean inside a spawned thread?
None of the above Solution works for me because in my case the parent thread responded for request back to the client and the request scoped object can't be referred in any worker threads.
I just made a work around to make above things work. I am using Spring Boot 2.2 and using customTaskExecutor with ContextAwareCallable just specified above.
Async Configuration:
#Bean(name = "cachedThreadPoolExecutor")
public Executor cachedThreadPoolExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ContextAwarePoolExecutor();
threadPoolTaskExecutor.setCorePoolSize(corePoolSize);
threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize);
threadPoolTaskExecutor.setQueueCapacity(queueCapacity);
threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
threadPoolTaskExecutor.setThreadNamePrefix("ThreadName-");
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
ContextAwarePoolExecutor:
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()));
}
}
Created Custom Context Aware Callable:
public class ContextAwareCallable<T> implements Callable<T> {
private Callable<T> task;
private CustomRequestScopeAttributes customRequestScopeAttributes;
private static final String requestScopedBean =
"scopedTarget.requestScopeBeanName";
public ContextAwareCallable(Callable<T> task, RequestAttributes context) {
this.task = task;
if (context != null) {
//This is Custom class implements RequestAttributes class
this.customRequestScopeAttributes = new CustomRequestScopeAttributes();
//Add the request scoped bean to Custom class
customRequestScopeAttributes.setAttribute
(requestScopedBean,context.getAttribute(requestScopedBean,0),0);
//Set that in RequestContextHolder and set as Inheritable as true
//Inheritable is used for setting the attributes in diffrent ThreadLocal objects.
RequestContextHolder.setRequestAttributes
(customRequestScopeAttributes,true);
}
}
#Override
public T call() throws Exception {
try {
return task.call();
} finally {
customRequestScopeAttributes.removeAttribute(requestScopedBean,0);
}
}
}
Custom class:
public class CustomRequestScopeAttributes implements RequestAttributes {
private Map<String, Object> requestAttributeMap = new HashMap<>();
#Override
public Object getAttribute(String name, int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST) {
return this.requestAttributeMap.get(name);
}
return null;
}
#Override
public void setAttribute(String name, Object value, int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST){
this.requestAttributeMap.put(name, value);
}
}
#Override
public void removeAttribute(String name, int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST) {
this.requestAttributeMap.remove(name);
}
}
#Override
public String[] getAttributeNames(int scope) {
if(scope== RequestAttributes.SCOPE_REQUEST) {
return this.requestAttributeMap.keySet().toArray(new String[0]);
}
return new String[0];
}
//Override all methods in the RequestAttributes Interface.
}
Finally add the Async annotation in the method needed.
#Async("cachedThreadPoolExecutor")
public void asyncMethod() {
anyService.execute() //This Service execution uses request scoped bean
}
With Spring-boot-2.0.3.REALEASE / spring-web-5.0.7, I've come up with below code working for #Async
Class that holds the ThreadLocal context.
import java.util.Map;
public class ThreadContextHolder {
private ThreadContextHolder() {}
private static final ThreadLocal<Map<String, Object>> ctx = new ThreadLocal<>();
public static Map<String, Object> getContext() {
return ctx.get();
}
public static void setContext(Map<String, Object> attrs) {
ctx.set(attrs);
}
public static void removeContext() {
ctx.remove();
}
}
Async config :
#Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
...
...
executor.setTaskDecorator(
runnable -> {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); // or currentRequestAttributes() if you want to fall back to JSF context.
Map<String, Object> map =
Arrays.stream(requestAttributes.getAttributeNames(0))
.collect(Collectors.toMap(r -> r, r -> requestAttributes.getAttribute(r, 0)));
return () -> {
try {
ThreadContextHolder.setContext(map);
runnable.run();
} finally {
ThreadContextHolder.removeContext();
}
};
});
executor.initialize();
return executor;
}
And from the async method :
#Async
public void asyncMethod() {
logger.info("{}", ThreadContextHolder.getContext().get("key"));
}
#Armadillo's answer motivated me to write the implementation for Runnable.
Custom implementation for TaskExecutor:
/**
* This custom ThreadPoolExecutor stores scoped/context information with the tasks.
*/
public class ContextAwareThreadPoolExecutor extends ThreadPoolTaskExecutor {
#Override
public Future<?> submit(Runnable task) {
return super.submit(new ContextAwareRunnable(task, RequestContextHolder.currentRequestAttributes()));
}
#Override
public ListenableFuture<?> submitListenable(Runnable task) {
return super.submitListenable(new ContextAwareRunnable(task, RequestContextHolder.currentRequestAttributes()));
}
}
Custom implementation for Runnable:
/**
* This custom Runnable class can use to make background threads context aware.
* It store and clear the context for the background threads.
*/
public class ContextAwareRunnable implements Runnable {
private Runnable task;
private RequestAttributes context;
public ContextAwareRunnable(Runnable task, RequestAttributes context) {
this.task = task;
// Keeps a reference to scoped/context information of parent thread.
// So original parent thread should wait for the background threads.
// Otherwise you should clone context as #Arun A's answer
this.context = context;
}
#Override
public void run() {
if (context != null) {
RequestContextHolder.setRequestAttributes(context);
}
try {
task.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
}
}
I solved this issue adding the following bean configuration
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="request">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
Update: the above solution does not clean up any objects associated with the threads as mention in spring's documentation. This alternative works for me: https://www.springbyexample.org/examples/custom-thread-scope-module.html
#Armadillo
Worked for me, many thanks.
As for Spring Security Context, there is more out-of-box solution and it worked for me either (found here How to set up Spring Security SecurityContextHolder strategy?)
In order to use SecurityContextHolder in child threads:
#Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class);
methodInvokingFactoryBean.setTargetMethod("setStrategyName");
methodInvokingFactoryBean.setArguments(new String[]{SecurityContextHolder.MODE_INHERITABLETHREADLOCAL});
return methodInvokingFactoryBean;
}
Here is a related answer for anyone who wants to use RequestScope with non blocking I/O commands in an API, as opposed to spinning up child threads that live on past the original HTTP request.
SPRING ASYNC AWAIT REQUEST SCOPE
It is possible to implement a custom scope in Spring which stores request scoped objects in the current HttpServletRequest object, so that objects can be accessed before and after 'await' statements:
Async Await Usage
HttpServletRequest based RequestScope
Full Java API Code Sample

Categories