We use multithreading and need the context of the calling thread in each sub-thread. We're using Spring 4.3.
For example:
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
CompletableFuture.supplyAsync(() -> {
...
try {
// take the security context from the caller
SecurityContextHolder.getContext().setAuthentication(authentication);
doOperations()
This approach works fine for the Security context. It's passed from the caller thread (rest endpoint) and passes it to each created completable future.
At a given Class in the call chain, I've following construct:
#Context
protected ProvisioningContext provisioningContext;
#Context
protected UriInfo uriInfo;
How do I pass all contexts correctly in the newly created thread?
Approaches like ThreadContext.getContext() are not working.
You can try to implement something similar to what I described here
Customize/Extend Spring's #Async support for shiro
That is:
Use Spring's #Async annotation to execute tasks in different threads
Implement a custom ThreadPoolTaskExecutor that associates the current context(s) with the to be executed task
Make spring use this task executor by implementing a AsyncConfigurer
If you want to use something like CompletableFuture.supplyAsync or local executor services, you will end up with a lot of duplicated code compared to approached outlined above - using a thread pool that is managed by spring.
Especially for something like the authentication context it is important to take care of removing the context from the thread as well. Otherwise the context might still be attached to the thread if it is recycled be the executor service to execute another task.
Related
I have such a controller and such a service class. Why am I getting this warning in IDEA - "Possibly blocking call in non-blocking context could lead to thread starvation" ?
#PostMapping(value = {"/create"})
public Mono<ResponseEntity<ResponseDto>> create(
#RequestBody RequestDto request) {
ResponseDto result = service.create(request);
return Mono.just(ResponseEntity.ok(result));
}
#Transactional
public ResponseDto create(RequestDto request) {
taskRepository.save(request);
return new ResponseDto("Ок");
}
This is apparently caused by the #Transactional annotation. When I remove it, the warning disappears. What is this problem and how can it be fixed?
p.s. this example is schematic. the real code is bigger.
The reactive process is contrary to the norm. You cannot use blocking elements here! With Tomcat, it creates a separate thread for each request so that the topic can be blocked. Reactive Netty will NOT create a new thread, just uses a fixed pool.
With the loose approach, you can think that if a process is waiting for a response, it gives the resource of that thread to another. If you block it, it won't be able to do that. Therefore, even with a single-threaded Netty, it can handle to serve multiple parallel requests.
Therefore, thread-based data storage also does not work properly, because another process can interfere or modify it. Therefore, reactive context is available instead.
There is a article to reactive transaction. I don't know it will be solution for you:
https://itnext.io/integrating-hibernate-reactive-with-spring-5427440607fe
I am writing a service where I want to expose an endpoint which will call another service and if the service call is successful then I want to send back the result to UI/ calling app.
In parallel before sending back the response, I want to execute/submit a task which should run in background and my call should not be dependent on success or failure of this task.
Before returning the response i want to do the-
executorService.execute(object);
This should not be a blocking call..
Any suggestion
Spring Async methods is the way to go here as was suggested in comments.
Some caveats:
Async methods can have different return types, its true that they can return CompletableFuture but this is in case if you called them from some background process and would like to wait/check for their execution status or perhaps, execute something else when the future is ready. In your case it seems that you want "fire-and-forget" behavior. So you should use void return type for your #Async annotated method.
Make sure that you place #EnableAsync. Under that hood it works in a way that it wraps the bean that has #Async methods with some sort of proxy, so the proxy is actually injected into your service. So #EnableAsync turns on this proxy generation mechanism. You can verify that this is the case usually in the debugger and checking the actual type of the injected reference object.
Consider customizing the the task executor to make sure that you're running the async methods with executor that matches your needs. For example, you won't probably want that every invocation of async method would spawn a new thread (and there is an executor that behaves like this). You can read about various executors here for example
Update
Code-wise you should do something like this:
public class MyAsyncHandler {
#Async
public void doAsyncJob(...) {
...
}
}
#Service
public class MyService {
#Autowired // or autowired constructor
private MyAsyncHandler asyncHandler;
public Result doMyMainJob(params) {
dao.saveInDB();
// do other synchronous stuff
Result res = prepareResult();
asyncHandler.doAsyncJob(); // this returns immediately
return res;
}
}
In latest versions of PlayFramework they started using CompletionStage as return type for controllers that will be used for async execution or in a nutshell if you return CompletionStage it is asynchronous execution...
Now when we know the work we submit to CF is a long running IO operation we need to pass a custom executor (otherwise it will be executed on FJP by default).
Each controller execution has a HTTP context which has in it all the request information also this context is necessary to have your EntityManagers if you use JPA...
If we just create custom ExecutorService and inject it in our controller to use in supplyAsync() we wont have all the context information.
Here is an example of some controller action returning CompletionStage
return supplyAsync(() -> {
doSomeWork();
}, executors.io); // this is a custom CachedThreadPool with daemon thread factory
}
and if we try to run something like this in doSomeWork()
Request request = request(); // getting request using Controller.request()
or use preinjected JPAAPI jpa field in controller
jpa.withTransaction(
() -> jpa.em() // we will get an exception here although we are wrapped in a transaction
...
);
exception like
No EntityManager bound to this thread. Try wrapping this call in JPAApi.withTransaction, or ensure that the HTTP context is setup on this thread.
As you can see the jpa code is wrapped in transaction but no context was found because this is a custome pure java threadpool.
What is the correct way to provide all the context information when using CompletableFuture and custom executor?
I also tried defining custom executors in application.conf and lookup them from actor system but i will end up having MessageDispatcher which although is backed by ExecutorService is not compatible with CompletableFuture (maybe i'm wrong? if so how to use it with CF?)
You can use play.libs.concurrent.HttpExecution.fromThread method:
An ExecutionContext that executes work on the given ExecutionContext. The current thread's context ClassLoader and Http.Context are captured when this method is called and preserved for all executed tasks.
So, the code would be something like:
java.util.concurrent.Executor executor = getExecutorFromSomewhere();
return supplyAsync(() -> {
doSomeWork();
}, play.libs.concurrent.HttpExecution.fromThread(executor));
Or, if you are using a scala.concurrent.ExecutionContext:
scala.concurrent.ExecutionContext ec = getExecutorContext();
return supplyAsync(() -> {
doSomeWork();
}, play.libs.concurrent.HttpExecution.fromThread(ec));
But I'm not entirely sure that will preserve the EntityManager for JPA.
Before adding CDI into our application I had created a resource that used the #Suspended AsyncResponse object to implement long polling for a chat client. What I did was create a new newSingleThreadExecutor() and submit a Runnable to it that used .wait(30000) on a message list until notification that a new message was sent. Inside that task I used the HttpServletRequest which was obtained using #Context and everything worked perfectly.
However once we added CDI to our application and even without making the resource class a bean (scanning only annotated beans and we didn't give it any scope annotation) I got a runtime exception that the request object INSIDE the Runnable task couldn't be accessed because of an illegal state exception:
Method threw 'java.lang.IllegalStateException' exception. Cannot evaluate com.sun.proxy.$Proxy74.toString()
I'm not really sure why this happens but I know it is CDI related since it refers to a proxy object. One guess is that the resource class itself has become CDI scoped and that scope can't be accessed from a different thread? I read somewhere that manually started threads are not managed and thus can't have access to any scope related objects. However how did this use to work until CDI was implemented?
Right now I THINK I've solved the issue (that is releasing the thread servicing request I/O and having a worker take over the waiting until notified) using jersey's #ManagedAsync annotation which supposedly has the whole method be run in an internal jersey executor service. Is this correct? Also in that case, is there any need of the AsyncResponse object?
EDIT: I have NOT solved the issue. #ManagedAsync worked when the resource class was not defined as a CDI bean. After making it #RequestScoped, whenever I try to call the method I get the following exception
org.jboss.weld.context.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped
I think this is because the request can end before the async thread has finished which means all scope objects (like HttpServletRequest) will be destroyed and thus we won't have access to them. Is there a way to used #ManagedAsync in a #RequestScoped bean and make use of #Context HttpServletRequest??
TL;DR:
How can I have access to a context resource inside a manually started thread?
Why did I have access to the request object before CDI was implemented?
Is it possible to use #ManagedAsync in a #RequestScoped cdi bean?
Old method:
#GET
#Path("method")
public void method(#Context HttpServletRequest request, #Suspended AsyncResponse ar) {
//request object was accessible here
Executors.newSingleTHreadExecutor().submit(() -> {
//request object was also accessible here but lost access after implementing CDI.
Object o = foo.bar(request);
ar.resume(Response.ok(o).build());
});
}
Current non-working method:
#GET
#Path("method")
#ManagedAsync
public void method(#Context HttpServletRequest request, #Suspended AsyncResponse ar) {
Object o = foo.bar(request);
ar.resume(Response.ok(o).build()); //Is there any point to this?
}
To answer your question - no. You cannot use async and request scoped objects. Async support is lacking in CDI - see also https://issues.jboss.org/browse/CDI-452
I am working on a project that works in two flavors with and without multi tenancy.
The project exposes a REST service which I would like to be asynchronous.
So my basic service looks like
#Component
#Path("/resouce")
#Consumes(MediaType.APPLICATION_JSON)
public class ResouceEndpoint {
#POST
#ManagedAsync
public void add(final Event event, #Suspended final AsyncResponse asyncResponse) {
resouce.insert (event);
asyncResponse.resume( Response.status(Response.Status.NO_CONTENT).build());
}
}
That works fine without multi tenancy and I get the benefits of the internal Jersey executor service for free. See #ManagedAsync
When I switch to multi tenancy I add a filter on the request that resolve the tenant id and place it on the thread local (in our case the HTTP thread).
When the processing chain hits the "add()" method above the current thread is the one provided by the Jersey executor service, so it does not include my tenant id.
I could think only on the following options to work around this issue.
Extend the ResouceEndpoint to MutliTenantResouceEndpoint and drop the #ManagedAsync
Using my own thread executor
public class MutliTenantResouceEndpoint extends ResouceEndpoint {
#POST
public void add(final Event event, #Suspended final AsyncResponse asyncResponse) {
final String tenantId = getTeantIdFromThreadLocal();
taskExecutor.submit(new Callable<Void>() {
#Override
public Void call() throws Exception {
setTeantIdToThreadLocal(tenantId);
browserEventsAnalyzer.insertEvent(event);
Response response = Response.status(Response.Status.NO_CONTENT).build();
asyncResponse.resume(response);
return null;
}
});
}
}
But this way I need to manage my own thread executor and it feel's like I am missing something here.
Any suggestion on a different approach?
Here are a handful of recommendations, in order.
For context, I've been using Jersey for 2 years, and faced this exact problem 18 months ago.
1. Stop using #ManagedAsync
If you have control over the http server that Jersey is running on, I would recommend you stop using #ManagedAsync.
Instead of setting up Jersey to return it's http handling thread immediately and offload real request work to a managed executor service thread, use something like Grizzly for your http server, and configure it to have a larger worker thread pool. This accomplishes the same thing, but pushes the async responsibility down a layer, below Jersey.
You'll run into many pain points over the course of a year if you use #ManagedAsync for any medium-to-large project. Here are some of them off the top of my head:
If any ContainerRequestFilter's hits an external service (e.g. an auth filter hits your security module, which hits the database) you will lose the benefits you thought you were gaining
If your DB chokes and that auth filter call takes 5 seconds, Jersey hasn't offloaded work to the async thread yet, so your main thread needed to receive a new conn is blocked
If you set up logback's MDC in a filter, and you want that context throughout your request, you'll need to set up the MDC again on the managed async thread
Resource methods are cryptic to new comers and ugly to read because:
they need an extra parameter
they return void, hiding their real response type
they can "return" anywhere, without any actual return statements
Swagger or other API doc tools cannot automatically document async resource endpoints
Guice or other DI frameworks may have trouble dealing with certain scope bindings and/or providers in async resource endpoints
2. Use #Context and ContainerRequest properties
This would involve involved calling requestContext.setProperty("tenant_id", tenantId) in your filter, then calling calling requestContext.getProperty("tenant_id") in your resource with a #Context injected request.
3. Use HK2 AOP instead of Jersey filters
This would involve setting up an HK2 binding of InterceptionService which has a MethodInterceptor that checks for managed async resource methods and manually executes all RequestScoped bound ContainerRequestFilters. Instead of your filters being registered with Jersey, you'd register them with HK2, to be run by the method interceptor.
I can add more detail and code samples to options 2/3 if you'd like, or give additional suggestions, but it would first be helpful to see more of your filter code, and I again suggest option 1 if possible.